@pixerate/editor 0.35.0 → 0.37.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/THIRD_PARTY_NOTICES.md +17 -0
- package/dist/canvas/index.cjs +25 -25
- package/dist/canvas/index.d.cts +3 -217
- package/dist/canvas/index.d.ts +3 -217
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-BGNPT5PE.cjs → chunk-GVB7ZPLZ.cjs} +24 -3
- package/dist/chunk-GVB7ZPLZ.cjs.map +1 -0
- package/dist/{chunk-EGPIVUEF.js → chunk-HPEKB352.js} +23 -2
- package/dist/chunk-HPEKB352.js.map +1 -0
- package/dist/{bezier-D8bsO-L0.d.cts → stroke-CWjFKal4.d.cts} +215 -1
- package/dist/{bezier-BvqVmWMg.d.ts → stroke-ysgaPpAo.d.ts} +215 -1
- package/dist/vector/index.cjs +2262 -388
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +704 -25
- package/dist/vector/index.d.ts +704 -25
- package/dist/vector/index.js +2107 -233
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-BGNPT5PE.cjs.map +0 -1
- package/dist/chunk-EGPIVUEF.js.map +0 -1
package/dist/vector/index.cjs
CHANGED
|
@@ -27,20 +27,994 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
var _chunkGVB7ZPLZcjs = require('../chunk-GVB7ZPLZ.cjs');
|
|
31
38
|
|
|
32
39
|
// src/vector/types.ts
|
|
33
|
-
var
|
|
40
|
+
var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
|
|
41
|
+
var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
|
|
42
|
+
var VECTOR_TOOLS = ["select", "direct", "pen", "pencil", "rect", "ellipse", "polygon", "star", "line", "scissors", "knife", "eraser", "width"];
|
|
34
43
|
var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
|
|
35
44
|
|
|
36
|
-
// src/vector/
|
|
45
|
+
// src/vector/corners.ts
|
|
37
46
|
var EPS = 1e-9;
|
|
47
|
+
var MIN_SINE = 1e-6;
|
|
48
|
+
var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
49
|
+
var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
50
|
+
var mul = (a, k) => ({ x: a.x * k, y: a.y * k });
|
|
51
|
+
var dot = (a, b) => a.x * b.x + a.y * b.y;
|
|
52
|
+
var cross = (a, b) => a.x * b.y - a.y * b.x;
|
|
53
|
+
var len = (a) => Math.hypot(a.x, a.y);
|
|
54
|
+
var pt = (p) => [p.x, p.y];
|
|
55
|
+
var vec = (p) => ({ x: p[0], y: p[1] });
|
|
56
|
+
var onAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS && Math.abs(h.y - a.y) < EPS;
|
|
57
|
+
var Side = class {
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
constructor(a, h1, h2, b) {
|
|
62
|
+
this.straight = onAnchor(a, h1) && onAnchor(b, h2);
|
|
63
|
+
this.curve = this.straight ? [pt(a), pt(b)] : [pt(a), pt(_nullishCoalesce(h1, () => ( a))), pt(_nullishCoalesce(h2, () => ( b))), pt(b)];
|
|
64
|
+
this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunkGVB7ZPLZcjs.segmentLength.call(void 0, this.curve);
|
|
65
|
+
}
|
|
66
|
+
/** Unit direction the side leaves the corner in (towards its first control point off the corner). */
|
|
67
|
+
direction() {
|
|
68
|
+
const p0 = vec(this.curve[0]);
|
|
69
|
+
for (const q of this.curve.slice(1)) {
|
|
70
|
+
const d = sub(vec(q), p0);
|
|
71
|
+
const l = len(d);
|
|
72
|
+
if (l > EPS) return mul(d, 1 / l);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/** The point `s` along the side from the corner: its parameter, position and the direction the side runs on in there. */
|
|
77
|
+
at(s, u) {
|
|
78
|
+
if (this.straight) {
|
|
79
|
+
const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
|
|
80
|
+
return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
|
|
81
|
+
}
|
|
82
|
+
const t = _chunkGVB7ZPLZcjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
|
|
83
|
+
const d = vec(_chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, this.curve, t));
|
|
84
|
+
const l = len(d);
|
|
85
|
+
return { t, p: vec(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function cornerOf(sp, s, i) {
|
|
89
|
+
const n = sp.anchors.length;
|
|
90
|
+
if (n < 2 || !sp.closed && (i === 0 || i === n - 1)) return null;
|
|
91
|
+
if (n === 2 && sp.closed) return null;
|
|
92
|
+
const a = sp.anchors[i];
|
|
93
|
+
if (a.type === "smooth" || a.type === "symmetric") return null;
|
|
94
|
+
const prev = sp.anchors[(i - 1 + n) % n];
|
|
95
|
+
const next = sp.anchors[(i + 1) % n];
|
|
96
|
+
const sideU = new Side(a, a.in, prev.out, prev);
|
|
97
|
+
const sideV = new Side(a, a.out, next.in, next);
|
|
98
|
+
const u = sideU.direction();
|
|
99
|
+
const v2 = sideV.direction();
|
|
100
|
+
if (!u || !v2) return null;
|
|
101
|
+
const lu = sideU.length;
|
|
102
|
+
const lv = sideV.length;
|
|
103
|
+
if (!(lu > EPS && lv > EPS) || !Number.isFinite(lu) || !Number.isFinite(lv)) return null;
|
|
104
|
+
const cos = Math.max(-1, Math.min(1, dot(u, v2)));
|
|
105
|
+
const sin = Math.abs(cross(u, v2));
|
|
106
|
+
if (sin <= MIN_SINE) return null;
|
|
107
|
+
return { subpath: s, index: i, at: { x: a.x, y: a.y }, u, v: v2, lu, lv, cos, sin, angle: Math.atan2(sin, cos) * 180 / Math.PI, maxRadius: Math.min(lu, lv) / 2 * sin / (1 + cos), sideU, sideV };
|
|
108
|
+
}
|
|
109
|
+
var strip = ({ sideU: _u, sideV: _v, ...info }) => info;
|
|
110
|
+
function vectorCornerAt(sp, index, subpath = 0) {
|
|
111
|
+
const c = cornerOf(sp, subpath, index);
|
|
112
|
+
return c ? strip(c) : null;
|
|
113
|
+
}
|
|
114
|
+
function vectorCorners(subpaths) {
|
|
115
|
+
const out = [];
|
|
116
|
+
subpaths.forEach((sp, s) => {
|
|
117
|
+
for (let i = 0; i < sp.anchors.length; i++) {
|
|
118
|
+
const c = cornerOf(sp, s, i);
|
|
119
|
+
if (c) out.push(strip(c));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
var cornerSetback = (c, r) => r * (1 + c.cos) / c.sin;
|
|
125
|
+
var fitCornerRadius = (c, radius) => Number.isFinite(radius) ? Math.min(Math.max(0, radius), c.maxRadius) : 0;
|
|
126
|
+
var cornerBisectorPoint = (c, k) => add(c.at, mul(add(c.u, c.v), k));
|
|
127
|
+
var cornerRadiusChange = (c, d) => dot(d, add(c.u, c.v)) * c.sin / (2 + 2 * c.cos);
|
|
128
|
+
function arcHandle(cos) {
|
|
129
|
+
const c = Math.max(-1, Math.min(1, cos));
|
|
130
|
+
const sinHalf = Math.sqrt(Math.max(0, (1 - c) / 2));
|
|
131
|
+
const cosHalf = Math.sqrt(Math.max(0, (1 + c) / 2));
|
|
132
|
+
return 4 / 3 * sinHalf / (1 + cosHalf);
|
|
133
|
+
}
|
|
134
|
+
function squareTo(a, b) {
|
|
135
|
+
const w = sub(b, mul(a, dot(a, b)));
|
|
136
|
+
const l = len(w);
|
|
137
|
+
return l > EPS ? mul(w, 1 / l) : { x: 0, y: 0 };
|
|
138
|
+
}
|
|
139
|
+
function cutCorner(c, radius, kind) {
|
|
140
|
+
const s = Math.min(cornerSetback(c, radius), Math.min(c.lu, c.lv) / 2);
|
|
141
|
+
if (!(s > EPS)) return null;
|
|
142
|
+
const a = c.sideU.at(s, c.u);
|
|
143
|
+
const b = c.sideV.at(s, c.v);
|
|
144
|
+
if (kind === "chamfer") return { tu: a.t, tv: b.t, start: a.p, out: null, in: null, end: b.p };
|
|
145
|
+
if (kind === "inverted") {
|
|
146
|
+
const h = s * arcHandle(dot(a.dir, b.dir));
|
|
147
|
+
return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(squareTo(a.dir, b.dir), h)), in: add(b.p, mul(squareTo(b.dir, a.dir), h)), end: b.p };
|
|
148
|
+
}
|
|
149
|
+
const d1 = mul(a.dir, -1);
|
|
150
|
+
const d2 = mul(b.dir, -1);
|
|
151
|
+
const turn = Math.acos(Math.max(-1, Math.min(1, -dot(d1, d2))));
|
|
152
|
+
const chord = len(sub(b.p, a.p));
|
|
153
|
+
const k = turn < 1e-9 ? chord / 3 : 4 / 3 * Math.tan(turn / 4) * chord / (2 * Math.sin(turn / 2));
|
|
154
|
+
if (!Number.isFinite(k)) return null;
|
|
155
|
+
return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(d1, k)), in: add(b.p, mul(d2, k)), end: b.p };
|
|
156
|
+
}
|
|
157
|
+
var radiusOf = (a) => {
|
|
158
|
+
const r = _optionalChain([a, 'access', _2 => _2.corner, 'optionalAccess', _3 => _3.radius]);
|
|
159
|
+
return typeof r === "number" && r > 0 && Number.isFinite(r) ? r : 0;
|
|
160
|
+
};
|
|
161
|
+
function hasVectorCorners(subpaths) {
|
|
162
|
+
return subpaths.some((sp) => sp.anchors.some((a) => radiusOf(a) > 0));
|
|
163
|
+
}
|
|
164
|
+
function expandVectorCornersOf(sp, subpath = 0) {
|
|
165
|
+
const n = sp.anchors.length;
|
|
166
|
+
const cuts = sp.anchors.map((a, i) => {
|
|
167
|
+
const r = radiusOf(a);
|
|
168
|
+
if (!r) return null;
|
|
169
|
+
const c = cornerOf(sp, subpath, i);
|
|
170
|
+
if (!c) return null;
|
|
171
|
+
const fitted = fitCornerRadius(c, r);
|
|
172
|
+
return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _4 => _4.corner, 'optionalAccess', _5 => _5.kind]), () => ( "round"))) : null;
|
|
173
|
+
});
|
|
174
|
+
if (!cuts.some(Boolean)) return { subpath: { closed: sp.closed, anchors: sp.anchors.map(plain) }, sources: sp.anchors.map((_, i) => i) };
|
|
175
|
+
const trimmed = sp.anchors.map((a, j) => {
|
|
176
|
+
const k = (j + 1) % n;
|
|
177
|
+
if (k === 0 && !sp.closed) return void 0;
|
|
178
|
+
const b = sp.anchors[k];
|
|
179
|
+
const from = cuts[j];
|
|
180
|
+
const to = cuts[k];
|
|
181
|
+
if (!from && !to) return void 0;
|
|
182
|
+
if (onAnchor(a, a.out) && onAnchor(b, b.in)) return { out: null, in: null };
|
|
183
|
+
const s0 = from ? from.tv : 0;
|
|
184
|
+
const s1 = to ? 1 - to.tu : 1;
|
|
185
|
+
const seg = [pt(a), pt(_nullishCoalesce(a.out, () => ( a))), pt(_nullishCoalesce(b.in, () => ( b))), pt(b)];
|
|
186
|
+
const part = _chunkGVB7ZPLZcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
|
|
187
|
+
return { out: vec(part[1]), in: vec(part[2]) };
|
|
188
|
+
});
|
|
189
|
+
const anchors = [];
|
|
190
|
+
const sources = [];
|
|
191
|
+
let closing = null;
|
|
192
|
+
const handleIn = (i, a) => {
|
|
193
|
+
const t = trimmed[(i - 1 + n) % n];
|
|
194
|
+
return t ? t.in : _nullishCoalesce(a.in, () => ( null));
|
|
195
|
+
};
|
|
196
|
+
const handleOut = (i, a) => {
|
|
197
|
+
const t = trimmed[i];
|
|
198
|
+
return t ? t.out : _nullishCoalesce(a.out, () => ( null));
|
|
199
|
+
};
|
|
200
|
+
sp.anchors.forEach((a, i) => {
|
|
201
|
+
const c = cuts[i];
|
|
202
|
+
if (!c) {
|
|
203
|
+
anchors.push(withHandles({ x: a.x, y: a.y, ...a.type ? { type: a.type } : {} }, handleIn(i, a), handleOut(i, a)));
|
|
204
|
+
sources.push(i);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const start = withHandles({ x: c.start.x, y: c.start.y }, handleIn(i, a), c.out);
|
|
208
|
+
const end = withHandles({ x: c.end.x, y: c.end.y }, c.in, handleOut(i, a));
|
|
209
|
+
if (i === 0 && sp.closed) closing = start;
|
|
210
|
+
else {
|
|
211
|
+
anchors.push(start);
|
|
212
|
+
sources.push(i);
|
|
213
|
+
}
|
|
214
|
+
anchors.push(end);
|
|
215
|
+
sources.push(i);
|
|
216
|
+
});
|
|
217
|
+
if (closing) {
|
|
218
|
+
anchors.push(closing);
|
|
219
|
+
sources.push(0);
|
|
220
|
+
}
|
|
221
|
+
return { subpath: { closed: sp.closed, anchors }, sources };
|
|
222
|
+
}
|
|
223
|
+
function withHandles(a, hin, hout) {
|
|
224
|
+
if (hin && !onAnchor(a, hin)) a.in = { x: hin.x, y: hin.y };
|
|
225
|
+
if (hout && !onAnchor(a, hout)) a.out = { x: hout.x, y: hout.y };
|
|
226
|
+
return a;
|
|
227
|
+
}
|
|
228
|
+
function plain(a) {
|
|
229
|
+
if (!("corner" in a)) return a;
|
|
230
|
+
const { corner: _c, ...rest } = a;
|
|
231
|
+
return rest;
|
|
232
|
+
}
|
|
233
|
+
function expandVectorCorners(subpaths) {
|
|
234
|
+
if (!hasVectorCorners(subpaths)) return subpaths;
|
|
235
|
+
return subpaths.map((sp, s) => sp.anchors.some((a) => radiusOf(a) > 0) ? expandVectorCornersOf(sp, s).subpath : sp);
|
|
236
|
+
}
|
|
237
|
+
function setAnchorCorner(a, radius, kind) {
|
|
238
|
+
const { corner, ...rest } = a;
|
|
239
|
+
const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
|
|
240
|
+
const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _6 => _6.kind])));
|
|
241
|
+
if (r <= 0 && !kind) return rest;
|
|
242
|
+
return { ...rest, corner: { radius: r, ...k && k !== "round" ? { kind: k } : {} } };
|
|
243
|
+
}
|
|
244
|
+
function nextVectorCornerKind(kind) {
|
|
245
|
+
return kind === "inverted" ? "chamfer" : kind === "chamfer" ? "round" : "inverted";
|
|
246
|
+
}
|
|
247
|
+
function setSubpathCorners(sp, radius, kind) {
|
|
248
|
+
return { closed: sp.closed, anchors: sp.anchors.map((a) => setAnchorCorner(a, radius, kind)) };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/vector/width.ts
|
|
252
|
+
var VECTOR_WIDTH_PROFILE_MAX_POINTS = 64;
|
|
253
|
+
var VECTOR_WIDTH_PROFILE_MAX_SCALE = 1e3;
|
|
254
|
+
var VECTOR_WIDTH_SAME_T = 1e-6;
|
|
255
|
+
var sym = (t, s) => ({ t, left: s, right: s });
|
|
256
|
+
var VECTOR_WIDTH_PRESETS = [
|
|
257
|
+
{ id: "uniform", label: "Uniform", points: [] },
|
|
258
|
+
{ id: "taperStart", label: "Taper start", points: [sym(0, 0), sym(1, 1)] },
|
|
259
|
+
{ id: "taperEnd", label: "Taper end", points: [sym(0, 1), sym(1, 0)] },
|
|
260
|
+
{ id: "taperBoth", label: "Taper both", points: [sym(0, 0), sym(0.5, 1), sym(1, 0)] },
|
|
261
|
+
// Full width at the ends, swelling to twice the width in the middle.
|
|
262
|
+
{ id: "bulge", label: "Bulge", points: [sym(0, 1), sym(0.5, 2), sym(1, 1)] },
|
|
263
|
+
// Full width at the ends, pinched to a quarter in the middle.
|
|
264
|
+
{ id: "pinch", label: "Pinch", points: [sym(0, 1), sym(0.5, 0.25), sym(1, 1)] },
|
|
265
|
+
// A round head a fifth of the way along, then a long taper to the end.
|
|
266
|
+
{ id: "teardrop", label: "Teardrop", points: [sym(0, 0), sym(0.2, 1), sym(1, 0)] },
|
|
267
|
+
// Two swells between narrow necks.
|
|
268
|
+
{ id: "wave", label: "Wave", points: [sym(0, 0.3), sym(0.25, 1), sym(0.5, 0.3), sym(0.75, 1), sym(1, 0.3)] },
|
|
269
|
+
// A pen stroke: a quick press, a slow lift.
|
|
270
|
+
{ id: "pressure", label: "Pressure", points: [sym(0, 0.15), sym(0.12, 0.85), sym(0.45, 1), sym(0.8, 0.7), sym(1, 0.05)] }
|
|
271
|
+
];
|
|
272
|
+
var VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESETS.map((p) => p.id);
|
|
273
|
+
var clean = (v2, max) => typeof v2 === "number" && Number.isFinite(v2) ? Math.min(Math.max(v2, 0), max) : null;
|
|
274
|
+
function sanitizeWidthProfile(points) {
|
|
275
|
+
if (!Array.isArray(points)) return [];
|
|
276
|
+
const out = [];
|
|
277
|
+
for (const p of points) {
|
|
278
|
+
if (!p || typeof p !== "object") continue;
|
|
279
|
+
const t = clean(p.t, 1);
|
|
280
|
+
const l = clean(p.left, VECTOR_WIDTH_PROFILE_MAX_SCALE);
|
|
281
|
+
const r = clean(p.right, VECTOR_WIDTH_PROFILE_MAX_SCALE);
|
|
282
|
+
if (t === null || l === null && r === null) continue;
|
|
283
|
+
out.push({ t, left: _nullishCoalesce(l, () => ( r)), right: _nullishCoalesce(r, () => ( l)) });
|
|
284
|
+
if (out.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) break;
|
|
285
|
+
}
|
|
286
|
+
return out.map((p, i) => [p, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([p]) => p);
|
|
287
|
+
}
|
|
288
|
+
function isUniformWidthProfile(points) {
|
|
289
|
+
return !_optionalChain([points, 'optionalAccess', _7 => _7.length]) || points.every((p) => p.left === 1 && p.right === 1);
|
|
290
|
+
}
|
|
291
|
+
function secant(pts, i, k) {
|
|
292
|
+
const a = pts[i];
|
|
293
|
+
const b = pts[i + 1];
|
|
294
|
+
return a && b && b.t - a.t > 1e-12 ? (b[k] - a[k]) / (b.t - a.t) : 0;
|
|
295
|
+
}
|
|
296
|
+
function tangent(pts, i, k) {
|
|
297
|
+
const last = pts.length - 1;
|
|
298
|
+
if (i <= 0) return secant(pts, 0, k);
|
|
299
|
+
if (i >= last) return secant(pts, last - 1, k);
|
|
300
|
+
const d0 = secant(pts, i - 1, k);
|
|
301
|
+
const d1 = secant(pts, i, k);
|
|
302
|
+
return d0 * d1 <= 0 ? 0 : 2 * d0 * d1 / (d0 + d1);
|
|
303
|
+
}
|
|
304
|
+
function interp(pts, t, k) {
|
|
305
|
+
const first = pts[0];
|
|
306
|
+
const last = pts[pts.length - 1];
|
|
307
|
+
if (!(t > first.t)) return first[k];
|
|
308
|
+
if (t >= last.t) return last[k];
|
|
309
|
+
let i = 0;
|
|
310
|
+
while (i + 1 < pts.length && pts[i + 1].t <= t) i++;
|
|
311
|
+
const a = pts[i];
|
|
312
|
+
const b = pts[i + 1];
|
|
313
|
+
const h = b.t - a.t;
|
|
314
|
+
if (h <= 1e-12) return b[k];
|
|
315
|
+
const u = (t - a.t) / h;
|
|
316
|
+
const u2 = u * u;
|
|
317
|
+
const u3 = u2 * u;
|
|
318
|
+
const w = (2 * u3 - 3 * u2 + 1) * a[k] + (u3 - 2 * u2 + u) * h * tangent(pts, i, k) + (3 * u2 - 2 * u3) * b[k] + (u3 - u2) * h * tangent(pts, i + 1, k);
|
|
319
|
+
return Math.max(0, w);
|
|
320
|
+
}
|
|
321
|
+
function widthProfileAt(points, t) {
|
|
322
|
+
if (!_optionalChain([points, 'optionalAccess', _8 => _8.length])) return { left: 1, right: 1 };
|
|
323
|
+
return { left: interp(points, t, "left"), right: interp(points, t, "right") };
|
|
324
|
+
}
|
|
325
|
+
function widthProfileMaxScale(points) {
|
|
326
|
+
if (!_optionalChain([points, 'optionalAccess', _9 => _9.length])) return 1;
|
|
327
|
+
return points.reduce((m, p) => Math.max(m, p.left, p.right), 0);
|
|
328
|
+
}
|
|
329
|
+
function widthProfilePreset(id) {
|
|
330
|
+
const p = VECTOR_WIDTH_PRESETS.find((q) => q.id === id);
|
|
331
|
+
return p ? p.points.map((q) => ({ ...q })) : null;
|
|
332
|
+
}
|
|
333
|
+
function widthProfilePresetId(points) {
|
|
334
|
+
if (isUniformWidthProfile(points)) return "uniform";
|
|
335
|
+
const same = (a, b) => a.length === b.length && a.every((p, i) => Math.abs(p.t - b[i].t) < 1e-9 && Math.abs(p.left - b[i].left) < 1e-9 && Math.abs(p.right - b[i].right) < 1e-9);
|
|
336
|
+
return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _10 => _10.find, 'call', _11 => _11((p) => p.points.length && same(points, p.points)), 'optionalAccess', _12 => _12.id]), () => ( "custom"));
|
|
337
|
+
}
|
|
338
|
+
function reverseWidthProfile(points) {
|
|
339
|
+
return points.map((p) => ({ t: 1 - p.t, left: p.right, right: p.left })).reverse();
|
|
340
|
+
}
|
|
341
|
+
function mirrorWidthProfile(points) {
|
|
342
|
+
return points.map((p) => ({ t: p.t, left: p.right, right: p.left }));
|
|
343
|
+
}
|
|
344
|
+
function editable(points) {
|
|
345
|
+
return _optionalChain([points, 'optionalAccess', _13 => _13.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
|
|
346
|
+
}
|
|
347
|
+
function insertWidthPoint(points, t, sides = {}, options = {}) {
|
|
348
|
+
const pts = editable(points);
|
|
349
|
+
const tt = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), 1);
|
|
350
|
+
const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _14 => _14.length]) ? points : null, tt);
|
|
351
|
+
const p = {
|
|
352
|
+
t: tt,
|
|
353
|
+
left: _nullishCoalesce(clean(sides.left, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.left)),
|
|
354
|
+
right: _nullishCoalesce(clean(sides.right, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.right))
|
|
355
|
+
};
|
|
356
|
+
const same = pts.findIndex((q) => Math.abs(q.t - tt) <= VECTOR_WIDTH_SAME_T);
|
|
357
|
+
if (same >= 0 && !options.step) {
|
|
358
|
+
pts[same] = { ...p, t: pts[same].t };
|
|
359
|
+
return { points: pts, index: same };
|
|
360
|
+
}
|
|
361
|
+
let i = 0;
|
|
362
|
+
while (i < pts.length && pts[i].t <= tt) i++;
|
|
363
|
+
if (pts.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { points: pts, index: Math.max(0, i - 1) };
|
|
364
|
+
pts.splice(i, 0, p);
|
|
365
|
+
return { points: pts, index: i };
|
|
366
|
+
}
|
|
367
|
+
function setWidthPoint(points, index, patch) {
|
|
368
|
+
const pts = points.map((p2) => ({ ...p2 }));
|
|
369
|
+
const p = pts[index];
|
|
370
|
+
if (!p) return pts;
|
|
371
|
+
if (typeof patch.t === "number" && Number.isFinite(patch.t)) {
|
|
372
|
+
const lo = index > 0 ? pts[index - 1].t : 0;
|
|
373
|
+
const hi = index < pts.length - 1 ? pts[index + 1].t : 1;
|
|
374
|
+
p.t = Math.min(Math.max(patch.t, lo), Math.max(hi, lo));
|
|
375
|
+
}
|
|
376
|
+
const l = clean(patch.left, VECTOR_WIDTH_PROFILE_MAX_SCALE);
|
|
377
|
+
const r = clean(patch.right, VECTOR_WIDTH_PROFILE_MAX_SCALE);
|
|
378
|
+
if (l !== null) p.left = l;
|
|
379
|
+
if (r !== null) p.right = r;
|
|
380
|
+
return pts;
|
|
381
|
+
}
|
|
382
|
+
function removeWidthPoints(points, indices) {
|
|
383
|
+
const drop = new Set(indices);
|
|
384
|
+
return points.filter((_, i) => !drop.has(i)).map((p) => ({ ...p }));
|
|
385
|
+
}
|
|
386
|
+
function ease(u, e) {
|
|
387
|
+
const x = Math.min(Math.max(u, 0), 1);
|
|
388
|
+
const round = Math.sqrt(Math.max(0, 1 - (1 - x) * (1 - x)));
|
|
389
|
+
return x + (round - x) * Math.min(Math.max(e, 0), 1);
|
|
390
|
+
}
|
|
391
|
+
function taperWaveFactor(t, taper, wave) {
|
|
392
|
+
let k = 1;
|
|
393
|
+
if (taper) {
|
|
394
|
+
let ls = Math.max(0, _nullishCoalesce(taper.startLength, () => ( 0)));
|
|
395
|
+
let le = Math.max(0, _nullishCoalesce(taper.endLength, () => ( 0)));
|
|
396
|
+
if (ls + le > 1) {
|
|
397
|
+
const f = 1 / (ls + le);
|
|
398
|
+
ls *= f;
|
|
399
|
+
le *= f;
|
|
400
|
+
}
|
|
401
|
+
const sw = Math.min(Math.max(_nullishCoalesce(taper.startWidth, () => ( 0)), 0), 1);
|
|
402
|
+
const ew = Math.min(Math.max(_nullishCoalesce(taper.endWidth, () => ( 0)), 0), 1);
|
|
403
|
+
if (ls > 0 && t < ls) k = Math.min(k, sw + (1 - sw) * ease(t / ls, _nullishCoalesce(taper.startEase, () => ( 0))));
|
|
404
|
+
if (le > 0 && t > 1 - le) k = Math.min(k, ew + (1 - ew) * ease((1 - t) / le, _nullishCoalesce(taper.endEase, () => ( 0))));
|
|
405
|
+
}
|
|
406
|
+
if (wave) {
|
|
407
|
+
const cycles = _nullishCoalesce(wave.cycles, () => ( 4));
|
|
408
|
+
const amount = Math.min(Math.max(_nullishCoalesce(wave.amount, () => ( 0.5)), 0), 1);
|
|
409
|
+
if (cycles > 0 && amount > 0) {
|
|
410
|
+
const ph = 2 * Math.PI * t * cycles + (_nullishCoalesce(wave.phase, () => ( 0))) * Math.PI / 180;
|
|
411
|
+
k *= 1 - amount * (0.5 - 0.5 * Math.cos(ph));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return k;
|
|
415
|
+
}
|
|
416
|
+
function generateWidthProfile(options) {
|
|
417
|
+
const cycles = options.wave ? Math.max(0, _nullishCoalesce(options.wave.cycles, () => ( 4))) : 0;
|
|
418
|
+
const n = Math.max(8, Math.min(VECTOR_WIDTH_PROFILE_MAX_POINTS * 4, _nullishCoalesce(options.samples, () => ( Math.ceil(32 + cycles * 16)))));
|
|
419
|
+
const raw = [];
|
|
420
|
+
for (let i = 0; i <= n; i++) raw.push([i / n, taperWaveFactor(i / n, options.taper, options.wave)]);
|
|
421
|
+
const ts = [_optionalChain([options, 'access', _15 => _15.taper, 'optionalAccess', _16 => _16.startLength]), _optionalChain([options, 'access', _17 => _17.taper, 'optionalAccess', _18 => _18.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
|
|
422
|
+
for (const t of ts) if (typeof t === "number" && t > 0 && t < 1) raw.push([t, taperWaveFactor(t, options.taper, options.wave)]);
|
|
423
|
+
raw.sort((a, b) => a[0] - b[0]);
|
|
424
|
+
const kept = simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.01)), VECTOR_WIDTH_PROFILE_MAX_POINTS);
|
|
425
|
+
return kept.map(([t, k]) => sym(t, k));
|
|
426
|
+
}
|
|
427
|
+
function simplifySamples(samples, tolerance, max) {
|
|
428
|
+
if (samples.length <= 2) return samples.slice();
|
|
429
|
+
const keep = new Array(samples.length).fill(false);
|
|
430
|
+
keep[0] = keep[samples.length - 1] = true;
|
|
431
|
+
const stack = [[0, samples.length - 1]];
|
|
432
|
+
while (stack.length) {
|
|
433
|
+
const [a, b] = stack.pop();
|
|
434
|
+
const [ta, va] = samples[a];
|
|
435
|
+
const [tb, vb] = samples[b];
|
|
436
|
+
let worst = -1;
|
|
437
|
+
let err = tolerance;
|
|
438
|
+
for (let i = a + 1; i < b; i++) {
|
|
439
|
+
const [t, v2] = samples[i];
|
|
440
|
+
const lin = tb - ta > 1e-12 ? va + (vb - va) * (t - ta) / (tb - ta) : va;
|
|
441
|
+
const e = Math.abs(v2 - lin);
|
|
442
|
+
if (e > err) {
|
|
443
|
+
err = e;
|
|
444
|
+
worst = i;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (worst >= 0) {
|
|
448
|
+
keep[worst] = true;
|
|
449
|
+
stack.push([a, worst], [worst, b]);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
let out = samples.filter((_, i) => keep[i]);
|
|
453
|
+
while (out.length > max) out = out.filter((_, i) => i === 0 || i === out.length - 1 || i % 2 === 0);
|
|
454
|
+
return out;
|
|
455
|
+
}
|
|
456
|
+
var VECTOR_PRESSURE_NOMINAL = 0.5;
|
|
457
|
+
function pressureWidthProfile(samples, options = {}) {
|
|
458
|
+
const raw = samples.filter((s) => Number.isFinite(s.t) && Number.isFinite(s.pressure)).map((s) => [Math.min(Math.max(s.t, 0), 1), Math.max(0.05, Math.min(1, Math.max(0, s.pressure)) / VECTOR_PRESSURE_NOMINAL)]);
|
|
459
|
+
if (raw.length < 2) return [];
|
|
460
|
+
if (raw.every((s) => Math.abs(s[1] - raw[0][1]) < 1e-6)) return [];
|
|
461
|
+
return simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.02)), VECTOR_WIDTH_PROFILE_MAX_POINTS).map(([t, k]) => sym(t, Math.round(k * 1e3) / 1e3));
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/vector/varstroke.ts
|
|
465
|
+
var SAMPLES_PER_SUBPATH = 128;
|
|
466
|
+
var MAX_DASHES = 5e3;
|
|
467
|
+
var sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];
|
|
468
|
+
var along = (p, v2, k) => [p[0] + v2[0] * k, p[1] + v2[1] * k];
|
|
469
|
+
var lerp = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
|
|
470
|
+
var dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
471
|
+
var unitOf = (v2) => {
|
|
472
|
+
const l = Math.hypot(v2[0], v2[1]);
|
|
473
|
+
return l > 0 ? [v2[0] / l, v2[1] / l] : [1, 0];
|
|
474
|
+
};
|
|
475
|
+
var leftNormal = (d) => [d[1], -d[0]];
|
|
476
|
+
function flatten(segments, closed, tol) {
|
|
477
|
+
const pts = [];
|
|
478
|
+
const corner = [];
|
|
479
|
+
const push = (p, isCorner) => {
|
|
480
|
+
const last = pts[pts.length - 1];
|
|
481
|
+
if (last && dist(last, p) <= 1e-9) {
|
|
482
|
+
corner[corner.length - 1] ||= isCorner;
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
pts.push(p);
|
|
486
|
+
corner.push(isCorner);
|
|
487
|
+
};
|
|
488
|
+
for (const seg of segments) {
|
|
489
|
+
const flat = _chunkGVB7ZPLZcjs.flattenChain.call(void 0, [seg], tol);
|
|
490
|
+
if (dist(seg[0], seg[seg.length - 1]) <= 1e-12 && flat.length > 1) flat.push(seg[seg.length - 1]);
|
|
491
|
+
flat.forEach((p, i) => push(p, i === 0 || i === flat.length - 1));
|
|
492
|
+
}
|
|
493
|
+
if (closed && pts.length > 2 && dist(pts[0], pts[pts.length - 1]) > 1e-9) push(pts[0], true);
|
|
494
|
+
if (pts.length < 2) return null;
|
|
495
|
+
let total = 0;
|
|
496
|
+
for (let i = 1; i < pts.length; i++) total += dist(pts[i - 1], pts[i]);
|
|
497
|
+
if (!(total > 0)) return null;
|
|
498
|
+
const step = Math.max(total / SAMPLES_PER_SUBPATH, tol);
|
|
499
|
+
const fine = [pts[0]];
|
|
500
|
+
const fineCorner = [corner[0]];
|
|
501
|
+
for (let i = 1; i < pts.length; i++) {
|
|
502
|
+
const n = Math.max(1, Math.ceil(dist(pts[i - 1], pts[i]) / step));
|
|
503
|
+
for (let k = 1; k <= n; k++) {
|
|
504
|
+
fine.push(k === n ? pts[i] : lerp(pts[i - 1], pts[i], k / n));
|
|
505
|
+
fineCorner.push(k === n ? corner[i] : false);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const s = [0];
|
|
509
|
+
for (let i = 1; i < fine.length; i++) s.push(s[i - 1] + dist(fine[i - 1], fine[i]));
|
|
510
|
+
return { pts: fine, s, corner: fineCorner, closed };
|
|
511
|
+
}
|
|
512
|
+
function dashIntervals(pattern, offset, len2) {
|
|
513
|
+
let pat = pattern.map((v2) => Number.isFinite(v2) ? Math.max(0, v2) : 0);
|
|
514
|
+
if (pat.length % 2 === 1) pat = [...pat, ...pat];
|
|
515
|
+
const period = pat.reduce((a, b) => a + b, 0);
|
|
516
|
+
if (pat.length < 2 || !(period > 1e-9) || !(len2 > 0)) return [[0, len2]];
|
|
517
|
+
let pos = ((Number.isFinite(offset) ? offset : 0) % period + period) % period;
|
|
518
|
+
let idx = 0;
|
|
519
|
+
while (pos >= pat[idx]) {
|
|
520
|
+
pos -= pat[idx];
|
|
521
|
+
idx = (idx + 1) % pat.length;
|
|
522
|
+
}
|
|
523
|
+
const out = [];
|
|
524
|
+
let s = 0;
|
|
525
|
+
let guard = 0;
|
|
526
|
+
while (s < len2 && guard++ < MAX_DASHES * pat.length) {
|
|
527
|
+
const e = Math.min(s + pat[idx] - pos, len2);
|
|
528
|
+
if (idx % 2 === 0 && e >= s) out.push([s, e]);
|
|
529
|
+
s += pat[idx] - pos;
|
|
530
|
+
pos = 0;
|
|
531
|
+
idx = (idx + 1) % pat.length;
|
|
532
|
+
}
|
|
533
|
+
return out;
|
|
534
|
+
}
|
|
535
|
+
function at(poly, t) {
|
|
536
|
+
let i = 1;
|
|
537
|
+
while (i < poly.pts.length - 1 && poly.s[i] <= t) i++;
|
|
538
|
+
const a = poly.s[i - 1];
|
|
539
|
+
const b = poly.s[i];
|
|
540
|
+
const u = b > a ? Math.min(Math.max((t - a) / (b - a), 0), 1) : 0;
|
|
541
|
+
return [lerp(poly.pts[i - 1], poly.pts[i], u), i];
|
|
542
|
+
}
|
|
543
|
+
function arcPoints(c, a0, sweep, r0, r1, tol) {
|
|
544
|
+
const r = Math.max(r0, r1);
|
|
545
|
+
if (!(r > tol * 0.5) || !Number.isFinite(sweep)) return [];
|
|
546
|
+
const stepAngle = 2 * Math.acos(Math.min(Math.max(1 - tol / r, -1), 1));
|
|
547
|
+
const n = Math.min(128, Math.max(2, Math.ceil(Math.abs(sweep) / Math.max(stepAngle, 1e-3))));
|
|
548
|
+
const out = [];
|
|
549
|
+
for (let k = 1; k < n; k++) {
|
|
550
|
+
const f = k / n;
|
|
551
|
+
const a = a0 + sweep * f;
|
|
552
|
+
const rr = r0 + (r1 - r0) * f;
|
|
553
|
+
out.push([c[0] + Math.cos(a) * rr, c[1] + Math.sin(a) * rr]);
|
|
554
|
+
}
|
|
555
|
+
return out;
|
|
556
|
+
}
|
|
557
|
+
function crossing(a0, a1, b0, b1) {
|
|
558
|
+
const r = sub2(a1, a0);
|
|
559
|
+
const q = sub2(b1, b0);
|
|
560
|
+
const den = r[0] * q[1] - r[1] * q[0];
|
|
561
|
+
if (Math.abs(den) < 1e-12) return null;
|
|
562
|
+
const w = sub2(b0, a0);
|
|
563
|
+
const t = (w[0] * q[1] - w[1] * q[0]) / den;
|
|
564
|
+
const u = (w[0] * r[1] - w[1] * r[0]) / den;
|
|
565
|
+
return t > 1e-9 && t <= 1 && u >= 0 && u < 1 - 1e-9 ? [a0[0] + r[0] * t, a0[1] + r[1] * t] : null;
|
|
566
|
+
}
|
|
567
|
+
function variableStrokeRings(path, options) {
|
|
568
|
+
const half = options.width / 2;
|
|
569
|
+
if (!(half > 0) || !Number.isFinite(half)) return [];
|
|
570
|
+
const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
|
|
571
|
+
const join = _nullishCoalesce(options.join, () => ( "miter"));
|
|
572
|
+
const cap = _nullishCoalesce(options.cap, () => ( "butt"));
|
|
573
|
+
const miterLimit = options.miterLimit && options.miterLimit >= 1 ? options.miterLimit : 4;
|
|
574
|
+
const rings = [];
|
|
575
|
+
const push = (out, p) => {
|
|
576
|
+
const last = out[out.length - 1];
|
|
577
|
+
if (!last || dist(last, p) > 1e-12) out.push(p);
|
|
578
|
+
};
|
|
579
|
+
for (const contour of _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path)) {
|
|
580
|
+
if (!contour.segments.length) continue;
|
|
581
|
+
const poly = flatten(contour.segments, contour.closed, Math.min(tol, half) / 4);
|
|
582
|
+
if (!poly) continue;
|
|
583
|
+
const len2 = poly.s[poly.s.length - 1];
|
|
584
|
+
const sides = (s) => {
|
|
585
|
+
const w = widthProfileAt(options.profile, s / len2);
|
|
586
|
+
return [w.left * half, w.right * half];
|
|
587
|
+
};
|
|
588
|
+
const intervals = _optionalChain([options, 'access', _19 => _19.dash, 'optionalAccess', _20 => _20.some, 'call', _21 => _21((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
|
|
589
|
+
const whole = intervals.length === 1 && intervals[0][0] <= 0 && intervals[0][1] >= len2;
|
|
590
|
+
for (const [a, b] of intervals) {
|
|
591
|
+
const [pa, ia] = at(poly, a);
|
|
592
|
+
const [pb, ib] = at(poly, b);
|
|
593
|
+
const pts = [{ p: pa, s: a, corner: poly.corner[0] && a <= 0 }];
|
|
594
|
+
for (let i = ia; i < ib; i++) pts.push({ p: poly.pts[i], s: poly.s[i], corner: poly.corner[i] });
|
|
595
|
+
pts.push({ p: pb, s: b, corner: false });
|
|
596
|
+
const run = pts.filter((q, i) => i === 0 || dist(q.p, pts[i - 1].p) > 1e-9);
|
|
597
|
+
if (run.length < 2) {
|
|
598
|
+
const [l, r] = sides(a);
|
|
599
|
+
const w2 = (l + r) / 2;
|
|
600
|
+
if (cap === "round" && w2 > 0) rings.push([[pa[0] + w2, pa[1]], ...arcPoints(pa, 0, 2 * Math.PI, w2, w2, tol)]);
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
const closed = whole && poly.closed;
|
|
604
|
+
const m = run.length - 1;
|
|
605
|
+
const dirs = [];
|
|
606
|
+
for (let i = 0; i < m; i++) dirs.push(unitOf(sub2(run[i + 1].p, run[i].p)));
|
|
607
|
+
const normals = dirs.map(leftNormal);
|
|
608
|
+
const w = run.map((q) => sides(q.s));
|
|
609
|
+
const side = (sigma) => {
|
|
610
|
+
const k = sigma > 0 ? 0 : 1;
|
|
611
|
+
const S = (i) => along(run[i].p, normals[i], sigma * w[i][k]);
|
|
612
|
+
const E = (i) => along(run[i + 1].p, normals[i], sigma * w[i + 1][k]);
|
|
613
|
+
const out = [S(0)];
|
|
614
|
+
for (let i = 0; i < m; i++) {
|
|
615
|
+
const seam = i === m - 1;
|
|
616
|
+
if (seam && !closed) {
|
|
617
|
+
push(out, E(i));
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
const j = seam ? 0 : i + 1;
|
|
621
|
+
const p = run[i + 1].p;
|
|
622
|
+
const d0 = dirs[i];
|
|
623
|
+
const d1 = dirs[j];
|
|
624
|
+
const cross2 = d0[0] * d1[1] - d0[1] * d1[0];
|
|
625
|
+
const dot2 = d0[0] * d1[0] + d0[1] * d1[1];
|
|
626
|
+
const r0 = w[i + 1][k];
|
|
627
|
+
const r1 = w[j][k];
|
|
628
|
+
const e = E(i);
|
|
629
|
+
const st = S(j);
|
|
630
|
+
if (Math.abs(cross2) < 1e-12 && dot2 > 0) {
|
|
631
|
+
push(out, e);
|
|
632
|
+
if (!seam) push(out, st);
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
const uturn = Math.abs(cross2) < 1e-12;
|
|
636
|
+
const outer = uturn ? sigma > 0 : cross2 * sigma > 0;
|
|
637
|
+
if (!outer) {
|
|
638
|
+
const x = seam ? null : crossing(out[out.length - 1], e, st, E(j));
|
|
639
|
+
if (x) push(out, x);
|
|
640
|
+
else {
|
|
641
|
+
push(out, e);
|
|
642
|
+
push(out, p);
|
|
643
|
+
if (!seam) push(out, st);
|
|
644
|
+
}
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
push(out, e);
|
|
648
|
+
const theta = uturn ? Math.PI : Math.atan2(cross2, dot2);
|
|
649
|
+
const r = (r0 + r1) / 2;
|
|
650
|
+
const kind = run[i + 1].corner || seam && poly.corner[0] ? join : "round";
|
|
651
|
+
if (r > 1e-9) {
|
|
652
|
+
if (kind === "round") {
|
|
653
|
+
if (r * (1 - Math.cos(Math.abs(theta) / 2)) > tol * 0.25) {
|
|
654
|
+
for (const q of arcPoints(p, Math.atan2(e[1] - p[1], e[0] - p[0]), theta, r0, r1, tol)) push(out, q);
|
|
655
|
+
}
|
|
656
|
+
} else if (kind === "miter") {
|
|
657
|
+
const halfCos = Math.cos(theta / 2);
|
|
658
|
+
const bis = [normals[i][0] + normals[j][0], normals[i][1] + normals[j][1]];
|
|
659
|
+
const bl = Math.hypot(bis[0], bis[1]);
|
|
660
|
+
if (halfCos > 1e-6 && 1 / halfCos <= miterLimit && bl > 1e-9) push(out, along(p, bis, sigma * r / halfCos / bl));
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
if (!seam) push(out, st);
|
|
664
|
+
}
|
|
665
|
+
if (closed && out.length > 1 && dist(out[0], out[out.length - 1]) <= 1e-12) out.pop();
|
|
666
|
+
return out;
|
|
667
|
+
};
|
|
668
|
+
const left = side(1);
|
|
669
|
+
const right = side(-1);
|
|
670
|
+
if (closed) {
|
|
671
|
+
rings.push(left, right.reverse());
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
const capPoints = (atStart) => {
|
|
675
|
+
if (cap === "butt") return [];
|
|
676
|
+
const i = atStart ? 0 : m;
|
|
677
|
+
const d = atStart ? dirs[0] : dirs[m - 1];
|
|
678
|
+
const [l, r] = w[i];
|
|
679
|
+
if (!(l + r > 1e-9)) return [];
|
|
680
|
+
const p = run[i].p;
|
|
681
|
+
const n = leftNormal(d);
|
|
682
|
+
const L = along(p, n, l);
|
|
683
|
+
const R = along(p, n, -r);
|
|
684
|
+
if (cap === "round") {
|
|
685
|
+
const a0 = Math.atan2(d[1], d[0]) + (atStart ? Math.PI / 2 : -Math.PI / 2);
|
|
686
|
+
return arcPoints(p, a0, Math.PI, atStart ? r : l, atStart ? l : r, tol);
|
|
687
|
+
}
|
|
688
|
+
const ext = (l + r) / 2;
|
|
689
|
+
const o = atStart ? [-d[0], -d[1]] : d;
|
|
690
|
+
return atStart ? [along(R, o, ext), along(L, o, ext)] : [along(L, o, ext), along(R, o, ext)];
|
|
691
|
+
};
|
|
692
|
+
const ring = [];
|
|
693
|
+
for (const q of [...left, ...capPoints(false), ...right.reverse(), ...capPoints(true)]) push(ring, q);
|
|
694
|
+
if (ring.length > 2) rings.push(ring);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return rings;
|
|
698
|
+
}
|
|
699
|
+
function variableStrokeOutline(path, options) {
|
|
700
|
+
const pieces = variableStrokeRings(path, options);
|
|
701
|
+
if (!pieces.length) return [];
|
|
702
|
+
const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
|
|
703
|
+
let polygons;
|
|
704
|
+
try {
|
|
705
|
+
polygons = _chunkGVB7ZPLZcjs.polygonBoolean.call(void 0, "union", [{ rings: pieces, fillRule: "nonzero" }]);
|
|
706
|
+
} catch (e2) {
|
|
707
|
+
return [];
|
|
708
|
+
}
|
|
709
|
+
const out = [];
|
|
710
|
+
for (const polygon2 of polygons) {
|
|
711
|
+
for (const ring of polygon2) {
|
|
712
|
+
if (ring.length < 3) continue;
|
|
713
|
+
const cubics = _chunkGVB7ZPLZcjs.fitCubics.call(void 0, ring, tol, { closed: true, cornerAngleDeg: 25 });
|
|
714
|
+
if (!cubics.length) continue;
|
|
715
|
+
out.push({ type: "M", x: cubics[0].p0[0], y: cubics[0].p0[1] });
|
|
716
|
+
cubics.forEach((c, i) => {
|
|
717
|
+
if (_chunkGVB7ZPLZcjs.isStraightCubic.call(void 0, c, tol * 0.02)) {
|
|
718
|
+
if (i < cubics.length - 1) out.push({ type: "L", x: c.p3[0], y: c.p3[1] });
|
|
719
|
+
} else out.push({ type: "C", x1: c.p1[0], y1: c.p1[1], x2: c.p2[0], y2: c.p2[1], x: c.p3[0], y: c.p3[1] });
|
|
720
|
+
});
|
|
721
|
+
out.push({ type: "Z" });
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return out;
|
|
725
|
+
}
|
|
726
|
+
function nearestStrokePosition(path, p, only) {
|
|
727
|
+
let best = null;
|
|
728
|
+
_chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).forEach((c, ci) => {
|
|
729
|
+
if (!c.segments.length || only !== void 0 && ci !== only) return;
|
|
730
|
+
const lengths = c.segments.map((s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s));
|
|
731
|
+
const total = lengths.reduce((a, b) => a + b, 0);
|
|
732
|
+
if (!(total > 0)) return;
|
|
733
|
+
let acc = 0;
|
|
734
|
+
c.segments.forEach((seg, si) => {
|
|
735
|
+
const near2 = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, seg, p);
|
|
736
|
+
if (!best || near2.distance < best.distance) {
|
|
737
|
+
const s = acc + _chunkGVB7ZPLZcjs.segmentLength.call(void 0, seg, 0, near2.t);
|
|
738
|
+
const tan = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, seg, near2.t);
|
|
739
|
+
const pt2 = seg.length === 2 ? lerp(seg[0], seg[1], near2.t) : evalSeg(seg, near2.t);
|
|
740
|
+
const n = leftNormal([tan[0], tan[1]]);
|
|
741
|
+
const side = (p[0] - pt2[0]) * n[0] + (p[1] - pt2[1]) * n[1] >= 0 ? "left" : "right";
|
|
742
|
+
best = { contour: ci, t: Math.min(Math.max(s / total, 0), 1), distance: near2.distance, side, point: pt2, tangent: [tan[0], tan[1]], length: total };
|
|
743
|
+
}
|
|
744
|
+
acc += lengths[si];
|
|
745
|
+
});
|
|
746
|
+
});
|
|
747
|
+
return best;
|
|
748
|
+
}
|
|
749
|
+
function evalSeg(seg, t) {
|
|
750
|
+
let pts = seg.map((q) => [q[0], q[1]]);
|
|
751
|
+
while (pts.length > 1) pts = pts.slice(1).map((q, i) => lerp(pts[i], q, t));
|
|
752
|
+
return pts[0];
|
|
753
|
+
}
|
|
754
|
+
function strokePositionPoint(path, index, t) {
|
|
755
|
+
const c = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path)[index];
|
|
756
|
+
if (!_optionalChain([c, 'optionalAccess', _22 => _22.segments, 'access', _23 => _23.length])) return null;
|
|
757
|
+
const lengths = c.segments.map((s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s));
|
|
758
|
+
const total = lengths.reduce((a, b) => a + b, 0);
|
|
759
|
+
let want = Math.min(Math.max(t, 0), 1) * total;
|
|
760
|
+
for (let i = 0; i < c.segments.length; i++) {
|
|
761
|
+
const seg = c.segments[i];
|
|
762
|
+
if (want <= lengths[i] + 1e-12 || i === c.segments.length - 1) {
|
|
763
|
+
let lo = 0;
|
|
764
|
+
let hi = 1;
|
|
765
|
+
const target = Math.min(want, lengths[i]);
|
|
766
|
+
for (let k = 0; k < 40; k++) {
|
|
767
|
+
const mid = (lo + hi) / 2;
|
|
768
|
+
if (_chunkGVB7ZPLZcjs.segmentLength.call(void 0, seg, 0, mid) < target) lo = mid;
|
|
769
|
+
else hi = mid;
|
|
770
|
+
}
|
|
771
|
+
const u = (lo + hi) / 2;
|
|
772
|
+
const tan = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, seg, u);
|
|
773
|
+
return { point: evalSeg(seg, u), tangent: [tan[0], tan[1]] };
|
|
774
|
+
}
|
|
775
|
+
want -= lengths[i];
|
|
776
|
+
}
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// src/vector/stroke.ts
|
|
781
|
+
var KAPPA = 0.5522847498307936;
|
|
782
|
+
var NOTCH = 0.7;
|
|
783
|
+
function isClosedBezierShape(path) {
|
|
784
|
+
const contours = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
|
|
785
|
+
return contours.length > 0 && contours.every((c) => c.closed);
|
|
786
|
+
}
|
|
787
|
+
function effectiveStrokeAlign(style, closedShape) {
|
|
788
|
+
const a = _nullishCoalesce(style.strokeAlign, () => ( "center"));
|
|
789
|
+
return closedShape ? a : "center";
|
|
790
|
+
}
|
|
791
|
+
function strokeWidthProfileOf(style) {
|
|
792
|
+
if (!_optionalChain([style, 'access', _24 => _24.strokeWidthProfile, 'optionalAccess', _25 => _25.length])) return null;
|
|
793
|
+
const p = sanitizeWidthProfile(style.strokeWidthProfile);
|
|
794
|
+
return isUniformWidthProfile(p) ? null : p;
|
|
795
|
+
}
|
|
796
|
+
function hasStrokeExtras(style) {
|
|
797
|
+
return (_nullishCoalesce(style.strokeAlign, () => ( "center"))) !== "center" || !!style.startArrow || !!style.endArrow || !!strokeWidthProfileOf(style);
|
|
798
|
+
}
|
|
799
|
+
function vectorStrokeLineArea(parts, style, tolerance) {
|
|
800
|
+
const profile = strokeWidthProfileOf(style);
|
|
801
|
+
const dash = _optionalChain([style, 'access', _26 => _26.strokeDasharray, 'optionalAccess', _27 => _27.length]) ? { dash: style.strokeDasharray } : {};
|
|
802
|
+
const join = _nullishCoalesce(style.strokeLinejoin, () => ( "miter"));
|
|
803
|
+
const cap = _nullishCoalesce(style.strokeLinecap, () => ( "butt"));
|
|
804
|
+
if (profile) return variableStrokeOutline(parts.line, { width: parts.drawWidth, profile, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
|
|
805
|
+
return _chunkGVB7ZPLZcjs.outlineStroke.call(void 0, parts.line, { width: parts.drawWidth, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
|
|
806
|
+
}
|
|
807
|
+
var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
|
|
808
|
+
function arrowheadWeight(style, end) {
|
|
809
|
+
const scale = end === "start" ? style.startArrowScale : style.endArrowScale;
|
|
810
|
+
const s = typeof scale === "number" && scale > 0 && Number.isFinite(scale) ? scale : 1;
|
|
811
|
+
return Math.max(0.25, (_nullishCoalesce(style.strokeWidth, () => ( 1))) * s);
|
|
812
|
+
}
|
|
813
|
+
function arrowheadInset(kind, hw, cap) {
|
|
814
|
+
const l = 4 * hw;
|
|
815
|
+
const solid = (back) => Math.max(back / 2, cap);
|
|
816
|
+
switch (kind) {
|
|
817
|
+
case "triangle":
|
|
818
|
+
case "circle":
|
|
819
|
+
case "square":
|
|
820
|
+
return solid(l);
|
|
821
|
+
case "arrow":
|
|
822
|
+
return solid(NOTCH * l);
|
|
823
|
+
case "bar":
|
|
824
|
+
return solid(0.75 * hw);
|
|
825
|
+
case "open":
|
|
826
|
+
return Math.max(hw * Math.sqrt(l * l + l * l / 4) / (l / 2), cap);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
var v = (x, y) => ({ x, y });
|
|
830
|
+
var plus = (a, b, k = 1) => ({ x: a.x + b.x * k, y: a.y + b.y * k });
|
|
831
|
+
var unit = (a) => {
|
|
832
|
+
const l = Math.hypot(a.x, a.y);
|
|
833
|
+
return l > 0 ? { x: a.x / l, y: a.y / l } : { x: 1, y: 0 };
|
|
834
|
+
};
|
|
835
|
+
function polygon(pts) {
|
|
836
|
+
return [{ type: "M", x: pts[0].x, y: pts[0].y }, ...pts.slice(1).map((p) => ({ type: "L", x: p.x, y: p.y })), { type: "Z" }];
|
|
837
|
+
}
|
|
838
|
+
function circle(c, r) {
|
|
839
|
+
const k = r * KAPPA;
|
|
840
|
+
const { x, y } = c;
|
|
841
|
+
return [
|
|
842
|
+
{ type: "M", x: x + r, y },
|
|
843
|
+
{ type: "C", x1: x + r, y1: y + k, x2: x + k, y2: y + r, x, y: y + r },
|
|
844
|
+
{ type: "C", x1: x - k, y1: y + r, x2: x - r, y2: y + k, x: x - r, y },
|
|
845
|
+
{ type: "C", x1: x - r, y1: y - k, x2: x - k, y2: y - r, x, y: y - r },
|
|
846
|
+
{ type: "C", x1: x + k, y1: y - r, x2: x + r, y2: y - k, x: x + r, y },
|
|
847
|
+
{ type: "Z" }
|
|
848
|
+
];
|
|
849
|
+
}
|
|
850
|
+
function arrowheadPath(kind, tip, dir, hw) {
|
|
851
|
+
const n = v(-dir.y, dir.x);
|
|
852
|
+
const l = 4 * hw;
|
|
853
|
+
const half = l / 2;
|
|
854
|
+
const base = plus(tip, dir, -l);
|
|
855
|
+
switch (kind) {
|
|
856
|
+
case "triangle":
|
|
857
|
+
return polygon([tip, plus(base, n, half), plus(base, n, -half)]);
|
|
858
|
+
case "arrow":
|
|
859
|
+
return polygon([tip, plus(base, n, half), plus(tip, dir, -NOTCH * l), plus(base, n, -half)]);
|
|
860
|
+
case "open": {
|
|
861
|
+
const t = hw;
|
|
862
|
+
const a = plus(base, n, half);
|
|
863
|
+
const b = plus(base, n, -half);
|
|
864
|
+
const ua = unit(plus(a, tip, -1));
|
|
865
|
+
const ub = unit(plus(b, tip, -1));
|
|
866
|
+
const na = v(-ua.y, ua.x);
|
|
867
|
+
const nb = v(ub.y, -ub.x);
|
|
868
|
+
const inner = plus(tip, dir, -(t * Math.hypot(a.x - tip.x, a.y - tip.y)) / half);
|
|
869
|
+
return polygon([tip, a, plus(a, na, t), inner, plus(b, nb, t), b]);
|
|
870
|
+
}
|
|
871
|
+
case "circle":
|
|
872
|
+
return circle(plus(tip, dir, -half), half);
|
|
873
|
+
case "square":
|
|
874
|
+
return polygon([plus(tip, n, half), plus(base, n, half), plus(base, n, -half), plus(tip, n, -half)]);
|
|
875
|
+
case "bar": {
|
|
876
|
+
const t = 0.75 * hw;
|
|
877
|
+
return polygon([plus(tip, n, half), plus(plus(tip, n, half), dir, -t), plus(plus(tip, n, -half), dir, -t), plus(tip, n, -half)]);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
var toVec = (p) => ({ x: p[0], y: p[1] });
|
|
882
|
+
var segLen = (s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s);
|
|
883
|
+
function endOf(segs, atStart) {
|
|
884
|
+
const long = (s2) => segLen(s2) > 1e-9;
|
|
885
|
+
if (atStart) {
|
|
886
|
+
const s2 = _nullishCoalesce(segs.find(long), () => ( segs[0]));
|
|
887
|
+
const t2 = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, s2, 0);
|
|
888
|
+
return { p: toVec(segs[0][0]), out: v(-t2[0], -t2[1]) };
|
|
889
|
+
}
|
|
890
|
+
const s = _nullishCoalesce([...segs].reverse().find(long), () => ( segs[segs.length - 1]));
|
|
891
|
+
const t = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, s, 1);
|
|
892
|
+
const last = segs[segs.length - 1];
|
|
893
|
+
return { p: toVec(last[last.length - 1]), out: v(t[0], t[1]) };
|
|
894
|
+
}
|
|
895
|
+
function pointAlong(segs, fromStart, dist4) {
|
|
896
|
+
const order = fromStart ? segs : [...segs].reverse();
|
|
897
|
+
let acc = 0;
|
|
898
|
+
for (const s of order) {
|
|
899
|
+
const l = segLen(s);
|
|
900
|
+
if (acc + l >= dist4) {
|
|
901
|
+
const want = fromStart ? dist4 - acc : l - (dist4 - acc);
|
|
902
|
+
const pts = _chunkGVB7ZPLZcjs.trimSegments.call(void 0, [s], 0, want);
|
|
903
|
+
const p = pts.length ? pts[0][pts[0].length - 1] : s[0];
|
|
904
|
+
return toVec(p);
|
|
905
|
+
}
|
|
906
|
+
acc += l;
|
|
907
|
+
}
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
function vectorStrokeParts(path, style) {
|
|
911
|
+
const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
|
|
912
|
+
const contours = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
|
|
913
|
+
const closedShape = contours.length > 0 && contours.every((c) => c.closed);
|
|
914
|
+
const align = effectiveStrokeAlign(style, closedShape);
|
|
915
|
+
const drawWidth = align === "center" ? width : width * 2;
|
|
916
|
+
const heads = [];
|
|
917
|
+
if (!contours.length || !style.startArrow && !style.endArrow) return { align, line: path, drawWidth, heads };
|
|
918
|
+
const first = contours[0];
|
|
919
|
+
const last = contours[contours.length - 1];
|
|
920
|
+
const cap = capExtent(style, width);
|
|
921
|
+
const tipMode = style.arrowAlign === "tip";
|
|
922
|
+
const trims = contours.map(() => [0, 0]);
|
|
923
|
+
const place = (end, contour, ci) => {
|
|
924
|
+
const kind = end === "start" ? style.startArrow : style.endArrow;
|
|
925
|
+
if (!kind || contour.closed) return;
|
|
926
|
+
const hw = arrowheadWeight(style, end);
|
|
927
|
+
const inset = arrowheadInset(kind, hw, cap);
|
|
928
|
+
const atStart = end === "start";
|
|
929
|
+
const { p, out } = endOf(contour.segments, atStart);
|
|
930
|
+
let tip;
|
|
931
|
+
let dir;
|
|
932
|
+
if (tipMode) {
|
|
933
|
+
const q = pointAlong(contour.segments, atStart, inset);
|
|
934
|
+
const d = q ? v(p.x - q.x, p.y - q.y) : null;
|
|
935
|
+
dir = d && Math.hypot(d.x, d.y) > 1e-9 ? unit(d) : out;
|
|
936
|
+
tip = p;
|
|
937
|
+
trims[ci][atStart ? 0 : 1] = inset;
|
|
938
|
+
} else {
|
|
939
|
+
tip = plus(p, out, inset);
|
|
940
|
+
dir = out;
|
|
941
|
+
}
|
|
942
|
+
heads.push({ end, kind, tip, dir, inset, path: arrowheadPath(kind, tip, dir, hw) });
|
|
943
|
+
};
|
|
944
|
+
place("start", first, 0);
|
|
945
|
+
place("end", last, contours.length - 1);
|
|
946
|
+
if (!tipMode || !heads.length) return { align, line: path, drawWidth, heads };
|
|
947
|
+
const trimmed = contours.map((c, i) => {
|
|
948
|
+
const [s0, s1] = trims[i];
|
|
949
|
+
if (!s0 && !s1) return c;
|
|
950
|
+
const lengths = c.segments.map(segLen);
|
|
951
|
+
const total = lengths.reduce((a, b) => a + b, 0);
|
|
952
|
+
return { closed: false, segments: total - s1 > s0 ? _chunkGVB7ZPLZcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
|
|
953
|
+
});
|
|
954
|
+
return { align, line: _chunkGVB7ZPLZcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
|
|
955
|
+
}
|
|
956
|
+
function vectorStrokeOutlinePath(path, style) {
|
|
957
|
+
const parts = vectorStrokeParts(path, style);
|
|
958
|
+
const stroke = vectorStrokeLineArea(parts, style);
|
|
959
|
+
const fill = { path, fillRule: _nullishCoalesce(style.fillRule, () => ( "nonzero")) };
|
|
960
|
+
let area = stroke;
|
|
961
|
+
if (parts.align === "inside") area = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
|
|
962
|
+
else if (parts.align === "outside") area = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
|
|
963
|
+
if (!parts.heads.length) return area;
|
|
964
|
+
return _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
|
|
965
|
+
}
|
|
966
|
+
function hitTestVectorStroke(path, p, style, tolerance = 0) {
|
|
967
|
+
const parts = vectorStrokeParts(path, style);
|
|
968
|
+
const point = [p.x, p.y];
|
|
969
|
+
const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
|
|
970
|
+
for (const h of parts.heads) {
|
|
971
|
+
if (_chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
|
|
972
|
+
}
|
|
973
|
+
if (!parts.line.length) return false;
|
|
974
|
+
const profile = strokeWidthProfileOf(style);
|
|
975
|
+
let d;
|
|
976
|
+
let reach = width / 2;
|
|
977
|
+
if (profile) {
|
|
978
|
+
const near2 = nearestStrokePosition(parts.line, point);
|
|
979
|
+
if (!near2) return false;
|
|
980
|
+
d = near2.distance;
|
|
981
|
+
const w = widthProfileAt(profile, near2.t);
|
|
982
|
+
reach = width / 2 * (near2.side === "left" ? w.left : w.right);
|
|
983
|
+
if (parts.align !== "center") reach *= 2;
|
|
984
|
+
const atEnd = near2.t <= 1e-9 || near2.t >= 1 - 1e-9;
|
|
985
|
+
if (atEnd && (style.strokeLinecap === "round" || style.strokeLinecap === "square") && d > reach) {
|
|
986
|
+
reach = Math.max(reach, width / 2 * (w.left + w.right) / 2);
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
d = _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, parts.line, point);
|
|
990
|
+
if (parts.align !== "center") reach = width;
|
|
991
|
+
}
|
|
992
|
+
if (parts.align === "center") return d <= reach + tolerance;
|
|
993
|
+
if (d > reach + tolerance) return false;
|
|
994
|
+
if (d <= tolerance) return true;
|
|
995
|
+
const inside = _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
|
|
996
|
+
return parts.align === "inside" ? inside : !inside;
|
|
997
|
+
}
|
|
998
|
+
function vectorStrokeReach(style) {
|
|
999
|
+
const width = (_nullishCoalesce(style.strokeWidth, () => ( 1))) * widthProfileMaxScale(strokeWidthProfileOf(style));
|
|
1000
|
+
const align = _nullishCoalesce(style.strokeAlign, () => ( "center"));
|
|
1001
|
+
let reach = align === "center" ? width / 2 : align === "outside" ? width : 0;
|
|
1002
|
+
if (style.strokeLinejoin !== "round" && style.strokeLinejoin !== "bevel") reach *= 4;
|
|
1003
|
+
for (const end of ["start", "end"]) {
|
|
1004
|
+
if (end === "start" ? style.startArrow : style.endArrow) reach = Math.max(reach, 8 * arrowheadWeight(style, end) + width);
|
|
1005
|
+
}
|
|
1006
|
+
return reach;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/vector/geometry.ts
|
|
1010
|
+
var EPS2 = 1e-9;
|
|
38
1011
|
var cloneAnchor = (a) => ({
|
|
39
1012
|
x: a.x,
|
|
40
1013
|
y: a.y,
|
|
41
1014
|
...a.in ? { in: { x: a.in.x, y: a.in.y } } : {},
|
|
42
1015
|
...a.out ? { out: { x: a.out.x, y: a.out.y } } : {},
|
|
43
|
-
...a.type && a.type !== "corner" ? { type: a.type } : {}
|
|
1016
|
+
...a.type && a.type !== "corner" ? { type: a.type } : {},
|
|
1017
|
+
...a.corner ? { corner: { ...a.corner } } : {}
|
|
44
1018
|
});
|
|
45
1019
|
var cloneSubpath = (s) => ({ anchors: s.anchors.map(cloneAnchor), closed: s.closed });
|
|
46
1020
|
function cloneVectorNode(node) {
|
|
@@ -58,7 +1032,7 @@ function hasPaint(paint) {
|
|
|
58
1032
|
return true;
|
|
59
1033
|
}
|
|
60
1034
|
var handleOf = (a, side) => _nullishCoalesce(a[side], () => ( { x: a.x, y: a.y }));
|
|
61
|
-
var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) <
|
|
1035
|
+
var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS2 && Math.abs(h.y - a.y) < EPS2;
|
|
62
1036
|
function subpathSegmentCount(sp) {
|
|
63
1037
|
const n = sp.anchors.length;
|
|
64
1038
|
if (n < 2) return 0;
|
|
@@ -72,9 +1046,9 @@ function subpathSegment(sp, i) {
|
|
|
72
1046
|
const c2 = handleOf(b, "in");
|
|
73
1047
|
return [[a.x, a.y], [c1.x, c1.y], [c2.x, c2.y], [b.x, b.y]];
|
|
74
1048
|
}
|
|
75
|
-
function subpathsToBezier(subpaths) {
|
|
1049
|
+
function subpathsToBezier(subpaths, options = {}) {
|
|
76
1050
|
const out = [];
|
|
77
|
-
for (const sp of subpaths) {
|
|
1051
|
+
for (const sp of options.corners === false ? subpaths : expandVectorCorners(subpaths)) {
|
|
78
1052
|
if (sp.anchors.length === 0) continue;
|
|
79
1053
|
const first = sp.anchors[0];
|
|
80
1054
|
out.push({ type: "M", x: first.x, y: first.y });
|
|
@@ -94,7 +1068,7 @@ function vectorNodeToBezier(node) {
|
|
|
94
1068
|
}
|
|
95
1069
|
function vectorPathData(input, precision = 3) {
|
|
96
1070
|
const subpaths = Array.isArray(input) ? input : input.subpaths;
|
|
97
|
-
return
|
|
1071
|
+
return _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
|
|
98
1072
|
}
|
|
99
1073
|
function inferAnchorType(a) {
|
|
100
1074
|
if (isOnAnchor(a, a.in) || isOnAnchor(a, a.out)) return "corner";
|
|
@@ -104,13 +1078,13 @@ function inferAnchorType(a) {
|
|
|
104
1078
|
const oy = a.out.y - a.y;
|
|
105
1079
|
const li = Math.hypot(ix, iy);
|
|
106
1080
|
const lo = Math.hypot(ox, oy);
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
if (Math.abs(
|
|
1081
|
+
const cross2 = (ix * oy - iy * ox) / (li * lo);
|
|
1082
|
+
const dot2 = (ix * ox + iy * oy) / (li * lo);
|
|
1083
|
+
if (Math.abs(cross2) > 1e-3 || dot2 > 0) return "corner";
|
|
110
1084
|
return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
|
|
111
1085
|
}
|
|
112
1086
|
function bezierToSubpaths(input) {
|
|
113
|
-
const path =
|
|
1087
|
+
const path = _chunkGVB7ZPLZcjs.toBezierPath.call(void 0, input);
|
|
114
1088
|
const out = [];
|
|
115
1089
|
let current = null;
|
|
116
1090
|
const last = () => current.anchors[current.anchors.length - 1];
|
|
@@ -207,14 +1181,14 @@ function nodeMatrix(node) {
|
|
|
207
1181
|
function vectorPathWorldBezier(node, parent = IDENTITY_MATRIX) {
|
|
208
1182
|
const m = multiplyMatrices(nodeMatrix(node), parent);
|
|
209
1183
|
const path = subpathsToBezier(node.subpaths);
|
|
210
|
-
return isIdentityMatrix(m) ? path :
|
|
1184
|
+
return isIdentityMatrix(m) ? path : _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, path, m);
|
|
211
1185
|
}
|
|
212
1186
|
function vectorNodeBounds(node, parent = IDENTITY_MATRIX) {
|
|
213
1187
|
if (node.type === "group") {
|
|
214
1188
|
const m = multiplyMatrices(nodeMatrix(node), parent);
|
|
215
1189
|
return unionBounds(node.children.map((c) => vectorNodeBounds(c, m)));
|
|
216
1190
|
}
|
|
217
|
-
const b =
|
|
1191
|
+
const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
|
|
218
1192
|
if (!b) return null;
|
|
219
1193
|
return { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY };
|
|
220
1194
|
}
|
|
@@ -248,9 +1222,13 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX) {
|
|
|
248
1222
|
const path = vectorPathWorldBezier(node, parent);
|
|
249
1223
|
if (path.length === 0) return false;
|
|
250
1224
|
const point = [p.x, p.y];
|
|
251
|
-
if (hasPaint(node.fill) &&
|
|
252
|
-
|
|
253
|
-
return
|
|
1225
|
+
if (hasPaint(node.fill) && _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
|
|
1226
|
+
if (hasPaint(node.stroke)) return hitTestVectorStroke(path, p, node, tolerance);
|
|
1227
|
+
return _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
|
|
1228
|
+
}
|
|
1229
|
+
function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
|
|
1230
|
+
if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return [];
|
|
1231
|
+
return vectorStrokeOutlinePath(vectorPathWorldBezier(node, parent), node);
|
|
254
1232
|
}
|
|
255
1233
|
function hitTestVectorNodes(nodes, p, tolerance = 0) {
|
|
256
1234
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
@@ -266,10 +1244,10 @@ function nearestSegment(node, p) {
|
|
|
266
1244
|
const count = subpathSegmentCount(sp);
|
|
267
1245
|
for (let i = 0; i < count; i++) {
|
|
268
1246
|
const seg = subpathSegment(sp, i);
|
|
269
|
-
const hit =
|
|
1247
|
+
const hit = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
|
|
270
1248
|
if (!best || hit.distance < best.distance) {
|
|
271
|
-
const
|
|
272
|
-
best = { subpath: s, segment: i, t: hit.t, point: { x:
|
|
1249
|
+
const pt2 = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, hit.t);
|
|
1250
|
+
best = { subpath: s, segment: i, t: hit.t, point: { x: pt2[0], y: pt2[1] }, distance: hit.distance };
|
|
273
1251
|
}
|
|
274
1252
|
}
|
|
275
1253
|
});
|
|
@@ -303,8 +1281,8 @@ function invertMatrix(m) {
|
|
|
303
1281
|
if (!det || !Number.isFinite(det)) return IDENTITY_MATRIX;
|
|
304
1282
|
return [d / det, -b / det, -c / det, a / det, (c * f - d * e) / det, (b * e - a * f) / det];
|
|
305
1283
|
}
|
|
306
|
-
function applyMatrixLinear(m,
|
|
307
|
-
return { x: m[0] *
|
|
1284
|
+
function applyMatrixLinear(m, v2) {
|
|
1285
|
+
return { x: m[0] * v2.x + m[2] * v2.y, y: m[1] * v2.x + m[3] * v2.y };
|
|
308
1286
|
}
|
|
309
1287
|
function matrixRotation(m) {
|
|
310
1288
|
const [a, b, c, d] = m;
|
|
@@ -328,8 +1306,10 @@ function bakeIntoGeometry(node, m) {
|
|
|
328
1306
|
if (isIdentityMatrix(m)) return node;
|
|
329
1307
|
if (node.type === "group") return { ...node, children: node.children.map((c) => transformVectorNode(c, m)) };
|
|
330
1308
|
const path = node;
|
|
1309
|
+
const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _28 => _28.strokeWidthProfile, 'optionalAccess', _29 => _29.length]);
|
|
331
1310
|
return {
|
|
332
1311
|
...path,
|
|
1312
|
+
...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
|
|
333
1313
|
subpaths: path.subpaths.map((sp) => ({
|
|
334
1314
|
closed: sp.closed,
|
|
335
1315
|
anchors: sp.anchors.map((a) => {
|
|
@@ -338,6 +1318,7 @@ function bakeIntoGeometry(node, m) {
|
|
|
338
1318
|
if (a.in) next.in = applyMatrix(m, a.in);
|
|
339
1319
|
if (a.out) next.out = applyMatrix(m, a.out);
|
|
340
1320
|
if (a.type) next.type = a.type;
|
|
1321
|
+
if (a.corner) next.corner = { ...a.corner };
|
|
341
1322
|
return next;
|
|
342
1323
|
})
|
|
343
1324
|
}))
|
|
@@ -389,12 +1370,12 @@ function moveAnchorHandle(a, side, pos, breakHandles = false) {
|
|
|
389
1370
|
if (breakHandles) delete next.type;
|
|
390
1371
|
const dx = pos.x - a.x;
|
|
391
1372
|
const dy = pos.y - a.y;
|
|
392
|
-
const
|
|
1373
|
+
const len2 = Math.hypot(dx, dy);
|
|
393
1374
|
if (type === "symmetric") next[other] = { x: a.x - dx, y: a.y - dy };
|
|
394
|
-
else if (type === "smooth" &&
|
|
1375
|
+
else if (type === "smooth" && len2 > EPS2) {
|
|
395
1376
|
const o = a[other];
|
|
396
|
-
const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) :
|
|
397
|
-
next[other] = { x: a.x - dx /
|
|
1377
|
+
const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) : len2;
|
|
1378
|
+
next[other] = { x: a.x - dx / len2 * olen, y: a.y - dy / len2 * olen };
|
|
398
1379
|
}
|
|
399
1380
|
if (isOnAnchor(next, next.in)) delete next.in;
|
|
400
1381
|
if (isOnAnchor(next, next.out)) delete next.out;
|
|
@@ -421,15 +1402,15 @@ function convertAnchor(sp, index, type) {
|
|
|
421
1402
|
else if (prev && next) dir = { x: next.x - prev.x, y: next.y - prev.y };
|
|
422
1403
|
else if (next) dir = { x: next.x - a.x, y: next.y - a.y };
|
|
423
1404
|
else if (prev) dir = { x: a.x - prev.x, y: a.y - prev.y };
|
|
424
|
-
if (!dir || Math.hypot(dir.x, dir.y) <
|
|
1405
|
+
if (!dir || Math.hypot(dir.x, dir.y) < EPS2) return { anchors, closed: sp.closed };
|
|
425
1406
|
const dl = Math.hypot(dir.x, dir.y);
|
|
426
1407
|
const ux = dir.x / dl;
|
|
427
1408
|
const uy = dir.y / dl;
|
|
428
1409
|
lenIn = a.in && !isOnAnchor(a, a.in) ? Math.hypot(a.in.x - a.x, a.in.y - a.y) : prev ? Math.hypot(prev.x - a.x, prev.y - a.y) / 3 : 0;
|
|
429
1410
|
lenOut = a.out && !isOnAnchor(a, a.out) ? Math.hypot(a.out.x - a.x, a.out.y - a.y) : next ? Math.hypot(next.x - a.x, next.y - a.y) / 3 : 0;
|
|
430
1411
|
if (type === "symmetric") lenIn = lenOut = (lenIn + lenOut) / 2 || Math.max(lenIn, lenOut);
|
|
431
|
-
if (lenIn >
|
|
432
|
-
if (lenOut >
|
|
1412
|
+
if (lenIn > EPS2) a.in = { x: a.x - ux * lenIn, y: a.y - uy * lenIn };
|
|
1413
|
+
if (lenOut > EPS2) a.out = { x: a.x + ux * lenOut, y: a.y + uy * lenOut };
|
|
433
1414
|
a.type = type;
|
|
434
1415
|
return { anchors, closed: sp.closed };
|
|
435
1416
|
}
|
|
@@ -439,7 +1420,7 @@ function insertAnchor(sp, segment, t) {
|
|
|
439
1420
|
const ia = segment;
|
|
440
1421
|
const ib = (segment + 1) % n;
|
|
441
1422
|
const seg = subpathSegment(sp, segment);
|
|
442
|
-
const [left, right] =
|
|
1423
|
+
const [left, right] = _chunkGVB7ZPLZcjs.splitSegment.call(void 0, seg, t);
|
|
443
1424
|
const mid = left[left.length - 1];
|
|
444
1425
|
const anchor = { x: mid[0], y: mid[1] };
|
|
445
1426
|
if (seg.length === 4) {
|
|
@@ -465,37 +1446,24 @@ function reverseSubpath(sp) {
|
|
|
465
1446
|
if (a.out) next.in = { ...a.out };
|
|
466
1447
|
if (a.in) next.out = { ...a.in };
|
|
467
1448
|
if (a.type) next.type = a.type;
|
|
1449
|
+
if (a.corner) next.corner = { ...a.corner };
|
|
468
1450
|
return next;
|
|
469
1451
|
});
|
|
470
1452
|
return { anchors, closed: sp.closed };
|
|
471
1453
|
}
|
|
472
|
-
var
|
|
473
|
-
function rectSubpath(b, radius = 0) {
|
|
1454
|
+
var KAPPA2 = 0.5522847498307936;
|
|
1455
|
+
function rectSubpath(b, radius = 0, kind) {
|
|
474
1456
|
const { x, y, width: w, height: h } = b;
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
return { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
|
|
478
|
-
}
|
|
479
|
-
const k = r * KAPPA;
|
|
480
|
-
const anchors = [
|
|
481
|
-
{ x: x + r, y, in: { x: x + r - k, y } },
|
|
482
|
-
{ x: x + w - r, y, out: { x: x + w - r + k, y } },
|
|
483
|
-
{ x: x + w, y: y + r, in: { x: x + w, y: y + r - k } },
|
|
484
|
-
{ x: x + w, y: y + h - r, out: { x: x + w, y: y + h - r + k } },
|
|
485
|
-
{ x: x + w - r, y: y + h, in: { x: x + w - r + k, y: y + h } },
|
|
486
|
-
{ x: x + r, y: y + h, out: { x: x + r - k, y: y + h } },
|
|
487
|
-
{ x, y: y + h - r, in: { x, y: y + h - r + k } },
|
|
488
|
-
{ x, y: y + r, out: { x, y: y + r - k } }
|
|
489
|
-
];
|
|
490
|
-
return { anchors, closed: true };
|
|
1457
|
+
const sp = { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
|
|
1458
|
+
return radius > 0 ? setSubpathCorners(sp, radius, kind) : sp;
|
|
491
1459
|
}
|
|
492
1460
|
function ellipseSubpath(b) {
|
|
493
1461
|
const rx = b.width / 2;
|
|
494
1462
|
const ry = b.height / 2;
|
|
495
1463
|
const cx = b.x + rx;
|
|
496
1464
|
const cy = b.y + ry;
|
|
497
|
-
const kx = rx *
|
|
498
|
-
const ky = ry *
|
|
1465
|
+
const kx = rx * KAPPA2;
|
|
1466
|
+
const ky = ry * KAPPA2;
|
|
499
1467
|
return {
|
|
500
1468
|
closed: true,
|
|
501
1469
|
anchors: [
|
|
@@ -543,15 +1511,80 @@ function roundSubpaths(subpaths, precision = 3) {
|
|
|
543
1511
|
}
|
|
544
1512
|
|
|
545
1513
|
// src/vector/svg.ts
|
|
1514
|
+
function vectorCornersAttribute(subpaths, precision = 3) {
|
|
1515
|
+
const out = [];
|
|
1516
|
+
subpaths.forEach(
|
|
1517
|
+
(sp, s) => sp.anchors.forEach((a, i) => {
|
|
1518
|
+
if (!a.corner || !(a.corner.radius > 0)) return;
|
|
1519
|
+
out.push(`${s}.${i}:${_chunkGVB7ZPLZcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
|
|
1520
|
+
})
|
|
1521
|
+
);
|
|
1522
|
+
return out.join(" ");
|
|
1523
|
+
}
|
|
1524
|
+
function applyVectorCornersAttribute(subpaths, value) {
|
|
1525
|
+
for (const item of value.trim().split(/\s+/)) {
|
|
1526
|
+
const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
|
|
1527
|
+
if (!m) continue;
|
|
1528
|
+
const a = _optionalChain([subpaths, 'access', _30 => _30[+m[1]], 'optionalAccess', _31 => _31.anchors, 'access', _32 => _32[+m[2]]]);
|
|
1529
|
+
const radius = parseFloat(m[3]);
|
|
1530
|
+
if (!a || !(radius > 0)) continue;
|
|
1531
|
+
const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
|
|
1532
|
+
a.corner = { radius, ...kind && kind !== "round" ? { kind } : {} };
|
|
1533
|
+
}
|
|
1534
|
+
return subpaths;
|
|
1535
|
+
}
|
|
546
1536
|
var escapeAttr = (value) => value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
547
1537
|
function vectorPaintToSvg(paint, gradientId) {
|
|
548
1538
|
if (!hasPaint(paint)) return { value: "none", gradient: null };
|
|
549
1539
|
if (typeof paint === "string") return { value: paint, gradient: null };
|
|
550
|
-
return { value: `url(#${gradientId})`, gradient:
|
|
1540
|
+
return { value: `url(#${gradientId})`, gradient: _chunkGVB7ZPLZcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
|
|
1541
|
+
}
|
|
1542
|
+
var partsCache = /* @__PURE__ */ new WeakMap();
|
|
1543
|
+
function vectorStrokeSvgParts(node, clipId, precision = 3) {
|
|
1544
|
+
if (!hasPaint(node.stroke) || !hasStrokeExtras(node)) return void 0;
|
|
1545
|
+
const hit = partsCache.get(node);
|
|
1546
|
+
if (hit && hit.clipId === clipId && hit.precision === precision) return hit.parts;
|
|
1547
|
+
const parts = computeStrokeSvgParts(node, clipId, precision);
|
|
1548
|
+
partsCache.set(node, { clipId, precision, parts });
|
|
1549
|
+
return parts;
|
|
1550
|
+
}
|
|
1551
|
+
function computeStrokeSvgParts(node, clipId, precision) {
|
|
1552
|
+
const path = subpathsToBezier(node.subpaths);
|
|
1553
|
+
const parts = vectorStrokeParts(path, node);
|
|
1554
|
+
const profiled = !!strokeWidthProfileOf(node);
|
|
1555
|
+
if (parts.align === "center" && !parts.heads.length && !profiled) return void 0;
|
|
1556
|
+
let clip = null;
|
|
1557
|
+
if (parts.align !== "center") {
|
|
1558
|
+
const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, path);
|
|
1559
|
+
const pad = vectorStrokeReach(node) + 1;
|
|
1560
|
+
const bounds = b ? { x: round2(b.minX - pad), y: round2(b.minY - pad), width: round2(b.maxX - b.minX + 2 * pad), height: round2(b.maxY - b.minY + 2 * pad) } : { x: 0, y: 0, width: 0, height: 0 };
|
|
1561
|
+
clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
|
|
1562
|
+
}
|
|
1563
|
+
return {
|
|
1564
|
+
align: parts.align,
|
|
1565
|
+
d: _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, parts.line, { precision }),
|
|
1566
|
+
width: parts.drawWidth,
|
|
1567
|
+
...profiled && (_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0 ? { outline: _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, vectorStrokeLineArea(parts, node), { precision }) } : {},
|
|
1568
|
+
clip,
|
|
1569
|
+
heads: parts.heads.map((h) => _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, h.path, { precision }))
|
|
1570
|
+
};
|
|
551
1571
|
}
|
|
1572
|
+
function widthProfileToAttribute(points, precision = 4) {
|
|
1573
|
+
return points.map((p) => [p.t, p.left, p.right].map((n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, precision)).join(",")).join(";");
|
|
1574
|
+
}
|
|
1575
|
+
function widthProfileFromAttribute(value) {
|
|
1576
|
+
if (!value) return [];
|
|
1577
|
+
return sanitizeWidthProfile(
|
|
1578
|
+
value.split(";").map((part) => {
|
|
1579
|
+
const [t, left, right] = part.split(",").map((n) => parseFloat(n));
|
|
1580
|
+
return { t, left, right: Number.isFinite(right) ? right : left };
|
|
1581
|
+
})
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
552
1585
|
function gradientMarkup(def) {
|
|
553
1586
|
const tag = def.type === "radial" ? "radialGradient" : "linearGradient";
|
|
554
|
-
const attrs = Object.entries(def.attrs).map(([k,
|
|
1587
|
+
const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
|
|
555
1588
|
const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
|
|
556
1589
|
return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
|
|
557
1590
|
}
|
|
@@ -561,11 +1594,11 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
561
1594
|
const pretty = options.pretty !== false;
|
|
562
1595
|
const defs = [];
|
|
563
1596
|
const lines = [];
|
|
564
|
-
const num = (n) =>
|
|
1597
|
+
const num = (n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, precision);
|
|
565
1598
|
const indent = (depth) => pretty ? " ".repeat(depth) : "";
|
|
566
1599
|
const common = (node) => {
|
|
567
1600
|
let out2 = ` id="${escapeAttr(node.id)}"`;
|
|
568
|
-
if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) =>
|
|
1601
|
+
if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
|
|
569
1602
|
if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
|
|
570
1603
|
if (node.visible === false) out2 += ` display="none"`;
|
|
571
1604
|
return out2;
|
|
@@ -582,15 +1615,61 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
582
1615
|
const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`);
|
|
583
1616
|
if (fill.gradient) defs.push(gradientMarkup(fill.gradient));
|
|
584
1617
|
if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
|
|
1618
|
+
const d = vectorPathData(node, precision);
|
|
1619
|
+
const corners2 = vectorCornersAttribute(node.subpaths, precision);
|
|
1620
|
+
const live = corners2 ? ` data-vector-d="${_chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
|
|
1621
|
+
const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
|
|
1622
|
+
const lineAttrs = (width) => {
|
|
1623
|
+
let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
|
|
1624
|
+
if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
|
|
1625
|
+
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
|
|
1626
|
+
if (_optionalChain([node, 'access', _33 => _33.strokeDasharray, 'optionalAccess', _34 => _34.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
|
|
1627
|
+
return out2;
|
|
1628
|
+
};
|
|
1629
|
+
const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
|
|
1630
|
+
if (!parts) {
|
|
1631
|
+
lines.push(`${indent(depth)}<path${common(node)} d="${d}"${fillAttrs}${stroke.value !== "none" ? lineAttrs(_nullishCoalesce(node.strokeWidth, () => ( 1))) : ""}${live}/>`);
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
let data = ` data-vector-path="" data-vector-stroke-width="${num(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
|
|
1635
|
+
if (parts.align !== "center") data += ` data-vector-stroke-align="${parts.align}"`;
|
|
1636
|
+
if (node.startArrow) data += ` data-vector-start-arrow="${node.startArrow}"`;
|
|
1637
|
+
if (node.endArrow) data += ` data-vector-end-arrow="${node.endArrow}"`;
|
|
1638
|
+
if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
|
|
1639
|
+
if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
|
|
1640
|
+
if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num(node.endArrowScale)}"`;
|
|
1641
|
+
const profile = strokeWidthProfileOf(node);
|
|
1642
|
+
if (profile) {
|
|
1643
|
+
data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
|
|
1644
|
+
if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
|
|
1645
|
+
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
|
|
1646
|
+
if (_optionalChain([node, 'access', _35 => _35.strokeDasharray, 'optionalAccess', _36 => _36.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
|
|
1647
|
+
}
|
|
1648
|
+
lines.push(`${indent(depth)}<g${common(node)}${data}>`);
|
|
1649
|
+
lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
|
|
1650
|
+
if (options.strokes === "outline") {
|
|
1651
|
+
const area = _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
|
|
1652
|
+
lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(stroke.value)}"/>`);
|
|
1653
|
+
} else {
|
|
1654
|
+
let clip = "";
|
|
1655
|
+
if (parts.clip) {
|
|
1656
|
+
const rule = node.fillRule === "evenodd" ? "evenodd" : "nonzero";
|
|
1657
|
+
const id = escapeAttr(parts.clip.id);
|
|
1658
|
+
if (parts.clip.kind === "clip") {
|
|
1659
|
+
defs.push(`<clipPath id="${id}"><path d="${d}" clip-rule="${rule}"/></clipPath>`);
|
|
1660
|
+
clip = ` clip-path="url(#${id})"`;
|
|
1661
|
+
} else {
|
|
1662
|
+
const b = parts.clip.bounds;
|
|
1663
|
+
const box = `x="${num(b.x)}" y="${num(b.y)}" width="${num(b.width)}" height="${num(b.height)}"`;
|
|
1664
|
+
defs.push(`<mask id="${id}" maskUnits="userSpaceOnUse" ${box}><rect ${box} fill="#fff"/><path d="${d}" fill="#000" fill-rule="${rule}"/></mask>`);
|
|
1665
|
+
clip = ` mask="url(#${id})"`;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
if (parts.outline !== void 0) lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${parts.outline}" fill="${escapeAttr(stroke.value)}"${clip}/>`);
|
|
1669
|
+
else lines.push(`${indent(depth + 1)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
|
|
1670
|
+
for (const h2 of parts.heads) lines.push(`${indent(depth + 1)}<path data-vector-part="head" d="${h2}" fill="${escapeAttr(stroke.value)}"/>`);
|
|
1671
|
+
}
|
|
1672
|
+
lines.push(`${indent(depth)}</g>`);
|
|
594
1673
|
};
|
|
595
1674
|
for (const node of doc.nodes) emit(node, 1);
|
|
596
1675
|
const w = num(doc.width);
|
|
@@ -733,7 +1812,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
|
|
|
733
1812
|
seen.add(id);
|
|
734
1813
|
let stops = readStops(el);
|
|
735
1814
|
const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
|
|
736
|
-
if (!stops.length && _optionalChain([href, 'optionalAccess',
|
|
1815
|
+
if (!stops.length && _optionalChain([href, 'optionalAccess', _37 => _37.startsWith, 'call', _38 => _38("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _39 => _39(href.slice(1), raw, seen), 'optionalAccess', _40 => _40.stops]), () => ( []));
|
|
737
1816
|
if (!stops.length) return null;
|
|
738
1817
|
if (el.name === "radialGradient") {
|
|
739
1818
|
return { type: "radial", cx: fraction(el.attrs.cx, 0.5), cy: fraction(el.attrs.cy, 0.5), r: fraction(el.attrs.r, 0.5), stops };
|
|
@@ -765,11 +1844,11 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
765
1844
|
const rawGradients = /* @__PURE__ */ new Map();
|
|
766
1845
|
collectGradients(root, rawGradients);
|
|
767
1846
|
const paint = (value) => {
|
|
768
|
-
const
|
|
769
|
-
const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(
|
|
1847
|
+
const v2 = value.trim();
|
|
1848
|
+
const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(v2);
|
|
770
1849
|
if (url) return _nullishCoalesce(resolveGradient(url[1], rawGradients), () => ( null));
|
|
771
|
-
if (
|
|
772
|
-
return
|
|
1850
|
+
if (v2 === "none" || v2 === "") return null;
|
|
1851
|
+
return v2;
|
|
773
1852
|
};
|
|
774
1853
|
const viewBox = numberList(root.attrs.viewBox);
|
|
775
1854
|
const width = viewBox.length === 4 ? viewBox[2] : length(root.attrs.width, 300);
|
|
@@ -779,8 +1858,13 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
779
1858
|
const shapeOf = (el) => {
|
|
780
1859
|
const a = el.attrs;
|
|
781
1860
|
switch (el.name) {
|
|
782
|
-
case "path":
|
|
1861
|
+
case "path": {
|
|
1862
|
+
if (a["data-vector-d"] && a["data-vector-corners"]) {
|
|
1863
|
+
const sharp = bezierToSubpaths(a["data-vector-d"]);
|
|
1864
|
+
if (sharp.length) return applyVectorCornersAttribute(sharp, a["data-vector-corners"]);
|
|
1865
|
+
}
|
|
783
1866
|
return a.d ? bezierToSubpaths(a.d) : null;
|
|
1867
|
+
}
|
|
784
1868
|
case "rect": {
|
|
785
1869
|
const w = length(a.width);
|
|
786
1870
|
const h = length(a.height);
|
|
@@ -814,6 +1898,65 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
814
1898
|
}
|
|
815
1899
|
return null;
|
|
816
1900
|
};
|
|
1901
|
+
const strokeStyle = (s) => ({
|
|
1902
|
+
...s.strokeWidth ? { strokeWidth: length(s.strokeWidth, 1) } : {},
|
|
1903
|
+
...s.strokeLinecap ? { strokeLinecap: s.strokeLinecap } : {},
|
|
1904
|
+
...s.strokeLinejoin ? { strokeLinejoin: s.strokeLinejoin } : {},
|
|
1905
|
+
...s.strokeDasharray && s.strokeDasharray !== "none" ? { strokeDasharray: numberList(s.strokeDasharray) } : {},
|
|
1906
|
+
...s.fillRule === "evenodd" ? { fillRule: "evenodd" } : {}
|
|
1907
|
+
});
|
|
1908
|
+
const placed = (node, m) => {
|
|
1909
|
+
if (m === IDENTITY_MATRIX) return node;
|
|
1910
|
+
const next = transformVectorNode(node, m);
|
|
1911
|
+
const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
|
|
1912
|
+
if (hasPaint(next.stroke) && strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) next.strokeWidth = (_nullishCoalesce(next.strokeWidth, () => ( 1))) * strokeScale;
|
|
1913
|
+
return next;
|
|
1914
|
+
};
|
|
1915
|
+
const mergedPath = (g, style, m, meta) => {
|
|
1916
|
+
const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
|
|
1917
|
+
const fillEl = part("fill");
|
|
1918
|
+
const subpaths = fillEl ? shapeOf(fillEl) : null;
|
|
1919
|
+
if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _41 => _41.length])) return null;
|
|
1920
|
+
const a = g.attrs;
|
|
1921
|
+
const fs = readStyle(fillEl, style);
|
|
1922
|
+
const strokeEl = part("stroke");
|
|
1923
|
+
const outlineEl = part("stroke-outline");
|
|
1924
|
+
const ss = strokeEl ? readStyle(strokeEl, style) : null;
|
|
1925
|
+
const width2 = length(a["data-vector-stroke-width"], ss ? length(ss.strokeWidth, 1) : 1);
|
|
1926
|
+
const arrow = (v2) => VECTOR_ARROWHEADS.includes(v2) ? v2 : void 0;
|
|
1927
|
+
const scale = (v2) => {
|
|
1928
|
+
const n = v2 === void 0 ? NaN : parseFloat(v2);
|
|
1929
|
+
return n > 0 ? n : void 0;
|
|
1930
|
+
};
|
|
1931
|
+
const align = a["data-vector-stroke-align"];
|
|
1932
|
+
const profile = widthProfileFromAttribute(a["data-vector-width-profile"]);
|
|
1933
|
+
const kept = (key, values) => values.includes(a[key]) ? a[key] : void 0;
|
|
1934
|
+
const keptCap = kept("data-vector-stroke-linecap", ["round", "square"]);
|
|
1935
|
+
const keptJoin = kept("data-vector-stroke-linejoin", ["round", "bevel"]);
|
|
1936
|
+
const keptDash = a["data-vector-stroke-dasharray"] ? numberList(a["data-vector-stroke-dasharray"]) : void 0;
|
|
1937
|
+
const node = {
|
|
1938
|
+
id: idFor(g),
|
|
1939
|
+
type: "path",
|
|
1940
|
+
...meta,
|
|
1941
|
+
subpaths,
|
|
1942
|
+
fill: paint(fs.fill),
|
|
1943
|
+
stroke: ss ? paint(ss.stroke) : outlineEl ? paint(readStyle(outlineEl, style).fill) : null,
|
|
1944
|
+
...ss ? strokeStyle(ss) : {},
|
|
1945
|
+
...fs.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
|
|
1946
|
+
strokeWidth: width2,
|
|
1947
|
+
...align === "inside" || align === "outside" ? { strokeAlign: align } : {},
|
|
1948
|
+
...arrow(a["data-vector-start-arrow"]) ? { startArrow: arrow(a["data-vector-start-arrow"]) } : {},
|
|
1949
|
+
...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
|
|
1950
|
+
...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
|
|
1951
|
+
...scale(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale(a["data-vector-start-arrow-scale"]) } : {},
|
|
1952
|
+
...scale(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale(a["data-vector-end-arrow-scale"]) } : {},
|
|
1953
|
+
...profile.length ? { strokeWidthProfile: profile } : {},
|
|
1954
|
+
...keptCap ? { strokeLinecap: keptCap } : {},
|
|
1955
|
+
...keptJoin ? { strokeLinejoin: keptJoin } : {},
|
|
1956
|
+
..._optionalChain([keptDash, 'optionalAccess', _42 => _42.length]) ? { strokeDasharray: keptDash } : {}
|
|
1957
|
+
};
|
|
1958
|
+
return placed(node, m);
|
|
1959
|
+
};
|
|
817
1960
|
const visit = (el, style, matrix) => {
|
|
818
1961
|
const out = [];
|
|
819
1962
|
for (const child of el.children) {
|
|
@@ -827,6 +1970,11 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
827
1970
|
...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
|
|
828
1971
|
...hidden ? { visible: false } : {}
|
|
829
1972
|
};
|
|
1973
|
+
if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
|
|
1974
|
+
const node2 = mergedPath(child, s, m, meta);
|
|
1975
|
+
if (node2) out.push(node2);
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
830
1978
|
if (child.name === "g" || child.name === "svg" || child.name === "a" || child.name === "switch") {
|
|
831
1979
|
const children = visit(child, s, m);
|
|
832
1980
|
if (!children.length) continue;
|
|
@@ -836,24 +1984,18 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
836
1984
|
}
|
|
837
1985
|
const subpaths = shapeOf(child);
|
|
838
1986
|
if (!subpaths || !subpaths.length) continue;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
};
|
|
852
|
-
if (m !== IDENTITY_MATRIX) {
|
|
853
|
-
node = transformVectorNode(node, m);
|
|
854
|
-
const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
|
|
855
|
-
if (hasPaint(node.stroke) && strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) node.strokeWidth = (_nullishCoalesce(node.strokeWidth, () => ( 1))) * strokeScale;
|
|
856
|
-
}
|
|
1987
|
+
const node = placed(
|
|
1988
|
+
{
|
|
1989
|
+
id: idFor(child),
|
|
1990
|
+
type: "path",
|
|
1991
|
+
...meta,
|
|
1992
|
+
subpaths,
|
|
1993
|
+
fill: child.name === "line" ? null : paint(s.fill),
|
|
1994
|
+
stroke: paint(s.stroke),
|
|
1995
|
+
...strokeStyle(s)
|
|
1996
|
+
},
|
|
1997
|
+
m
|
|
1998
|
+
);
|
|
857
1999
|
out.push(node);
|
|
858
2000
|
}
|
|
859
2001
|
return out;
|
|
@@ -865,7 +2007,7 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
865
2007
|
// src/vector/snap.ts
|
|
866
2008
|
var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
|
|
867
2009
|
function constrainToLeave(leave, p) {
|
|
868
|
-
if (!_optionalChain([leave, 'optionalAccess',
|
|
2010
|
+
if (!_optionalChain([leave, 'optionalAccess', _43 => _43.constrain])) return p;
|
|
869
2011
|
const dx = p.x - leave.at.x;
|
|
870
2012
|
const dy = p.y - leave.at.y;
|
|
871
2013
|
switch (leave.constrain) {
|
|
@@ -876,9 +2018,9 @@ function constrainToLeave(leave, p) {
|
|
|
876
2018
|
return { x: leave.at.x + Math.sign(dx || 1) * s, y: leave.at.y + Math.sign(dy || 1) * s };
|
|
877
2019
|
}
|
|
878
2020
|
default: {
|
|
879
|
-
const
|
|
2021
|
+
const len2 = Math.hypot(dx, dy);
|
|
880
2022
|
const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
|
|
881
|
-
return { x: leave.at.x + Math.cos(a) *
|
|
2023
|
+
return { x: leave.at.x + Math.cos(a) * len2, y: leave.at.y + Math.sin(a) * len2 };
|
|
882
2024
|
}
|
|
883
2025
|
}
|
|
884
2026
|
}
|
|
@@ -887,24 +2029,24 @@ var CROSSING_PAIRS = 32;
|
|
|
887
2029
|
var CURVE_SAMPLES = 48;
|
|
888
2030
|
var BISECT = 50;
|
|
889
2031
|
var EQUAL = 1e-6;
|
|
890
|
-
function bound(sorted, key,
|
|
2032
|
+
function bound(sorted, key, v2, strict) {
|
|
891
2033
|
let lo = 0;
|
|
892
2034
|
let hi = sorted.length;
|
|
893
2035
|
while (lo < hi) {
|
|
894
2036
|
const mid = lo + hi >>> 1;
|
|
895
2037
|
const k = key(sorted[mid]);
|
|
896
|
-
if (strict ? k <=
|
|
2038
|
+
if (strict ? k <= v2 : k < v2) lo = mid + 1;
|
|
897
2039
|
else hi = mid;
|
|
898
2040
|
}
|
|
899
2041
|
return lo;
|
|
900
2042
|
}
|
|
901
|
-
function within(sorted, key,
|
|
902
|
-
return sorted.slice(bound(sorted, key,
|
|
2043
|
+
function within(sorted, key, v2, tol) {
|
|
2044
|
+
return sorted.slice(bound(sorted, key, v2 - tol, false), bound(sorted, key, v2 + tol, true));
|
|
903
2045
|
}
|
|
904
|
-
var linesNear = (lines,
|
|
2046
|
+
var linesNear = (lines, v2, tol) => within(lines, (l) => l.v, v2, tol);
|
|
905
2047
|
var distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
906
2048
|
var toPoint = (p) => [p.x, p.y];
|
|
907
|
-
var
|
|
2049
|
+
var toVec2 = (p) => ({ x: p[0], y: p[1] });
|
|
908
2050
|
var LABELS = {
|
|
909
2051
|
anchor: "anchor",
|
|
910
2052
|
centre: "centre",
|
|
@@ -976,7 +2118,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
976
2118
|
moving.set(r.nodeId, set);
|
|
977
2119
|
}
|
|
978
2120
|
const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
|
|
979
|
-
t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access',
|
|
2121
|
+
t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _44 => _44.get, 'call', _45 => _45(id), 'optionalAccess', _46 => _46.has, 'call', _47 => _47(`${s}:${i}`)]), true);
|
|
980
2122
|
return t.index();
|
|
981
2123
|
}
|
|
982
2124
|
/**
|
|
@@ -1022,7 +2164,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1022
2164
|
const count = subpathSegmentCount(sp);
|
|
1023
2165
|
for (let i = 0; i < count && budget > 0; i++) {
|
|
1024
2166
|
if (moving(i) || moving((i + 1) % n)) continue;
|
|
1025
|
-
const seg = subpathSegment(sp, i).map((q) => toPoint(applyMatrix(m,
|
|
2167
|
+
const seg = subpathSegment(sp, i).map((q) => toPoint(applyMatrix(m, toVec2(q))));
|
|
1026
2168
|
let minX = Infinity;
|
|
1027
2169
|
let minY = Infinity;
|
|
1028
2170
|
let maxX = -Infinity;
|
|
@@ -1094,7 +2236,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1094
2236
|
}
|
|
1095
2237
|
const b = vectorNodeBounds(n, parent);
|
|
1096
2238
|
const f = nodeFrame(n, parent, b);
|
|
1097
|
-
if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess',
|
|
2239
|
+
if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _48 => _48.centre]), () => ( null)));
|
|
1098
2240
|
}
|
|
1099
2241
|
};
|
|
1100
2242
|
visit(doc.nodes, IDENTITY_MATRIX);
|
|
@@ -1131,7 +2273,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1131
2273
|
*/
|
|
1132
2274
|
snapFrom(p, leave, tol) {
|
|
1133
2275
|
const none = (q) => ({ point: q, kind: null, x: false, y: false, lines: [], labels: [] });
|
|
1134
|
-
const
|
|
2276
|
+
const at2 = (q, kind, lines2 = []) => ({
|
|
1135
2277
|
point: q,
|
|
1136
2278
|
kind,
|
|
1137
2279
|
x: true,
|
|
@@ -1139,23 +2281,23 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1139
2281
|
lines: lines2,
|
|
1140
2282
|
labels: [this.label(q, kind)]
|
|
1141
2283
|
});
|
|
1142
|
-
if (_optionalChain([leave, 'optionalAccess',
|
|
2284
|
+
if (_optionalChain([leave, 'optionalAccess', _49 => _49.constrain])) {
|
|
1143
2285
|
const q = constrainToLeave(leave, p);
|
|
1144
2286
|
const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
|
|
1145
2287
|
return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
|
|
1146
2288
|
}
|
|
1147
2289
|
const point = this.nearestPoint(p, tol);
|
|
1148
|
-
if (point) return
|
|
2290
|
+
if (point) return at2(point.p, point.kind);
|
|
1149
2291
|
const near2 = this.segmentsNear(p, tol);
|
|
1150
|
-
const
|
|
1151
|
-
if (
|
|
2292
|
+
const crossing2 = this.crossing(near2, p, tol);
|
|
2293
|
+
if (crossing2) return at2(crossing2, "intersect");
|
|
1152
2294
|
if (leave && leave.curves !== false) {
|
|
1153
2295
|
const foot = this.curvePoint(near2, leave.at, p, tol);
|
|
1154
|
-
if (foot) return
|
|
2296
|
+
if (foot) return at2(foot.point, foot.kind, [line("construction", leave.at, foot.point)]);
|
|
1155
2297
|
}
|
|
1156
2298
|
const on = this.onSegment(near2);
|
|
1157
|
-
if (on) return
|
|
1158
|
-
if (_optionalChain([leave, 'optionalAccess',
|
|
2299
|
+
if (on) return at2(on, "path");
|
|
2300
|
+
if (_optionalChain([leave, 'optionalAccess', _50 => _50.angles, 'optionalAccess', _51 => _51.length])) {
|
|
1159
2301
|
const c = construction(leave, p, tol);
|
|
1160
2302
|
if (c) {
|
|
1161
2303
|
const hit = this.slide(leave.at, c.dir, c.point, tol, false);
|
|
@@ -1166,9 +2308,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1166
2308
|
return { point: c.point, kind: "construction", x: true, y: true, lines: [line("construction", leave.at, c.point)], labels: [] };
|
|
1167
2309
|
}
|
|
1168
2310
|
}
|
|
1169
|
-
const nearest = (lines2,
|
|
2311
|
+
const nearest = (lines2, v2) => {
|
|
1170
2312
|
let best = null;
|
|
1171
|
-
for (const l of linesNear(lines2,
|
|
2313
|
+
for (const l of linesNear(lines2, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.v - v2)) best = l;
|
|
1172
2314
|
return best;
|
|
1173
2315
|
};
|
|
1174
2316
|
const bx = nearest(this.xs, p.x);
|
|
@@ -1201,7 +2343,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1201
2343
|
* the nearest target line crosses it, at most twice `tol` away: where it lands and the
|
|
1202
2344
|
* target point that line comes from.
|
|
1203
2345
|
*/
|
|
1204
|
-
slide(
|
|
2346
|
+
slide(at2, dir, q, tol, ray) {
|
|
1205
2347
|
const len2 = dir.x * dir.x + dir.y * dir.y;
|
|
1206
2348
|
if (!Number.isFinite(len2) || len2 <= 1e-18) return null;
|
|
1207
2349
|
const reach = 2 * tol;
|
|
@@ -1209,10 +2351,10 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1209
2351
|
for (const vertical of [true, false]) {
|
|
1210
2352
|
const d = vertical ? dir.x : dir.y;
|
|
1211
2353
|
if (Math.abs(d) <= 1e-9) continue;
|
|
1212
|
-
const a = vertical ?
|
|
2354
|
+
const a = vertical ? at2.x : at2.y;
|
|
1213
2355
|
for (const l of linesNear(vertical ? this.xs : this.ys, vertical ? q.x : q.y, reach)) {
|
|
1214
2356
|
const s = (l.v - a) / d;
|
|
1215
|
-
const hit = vertical ? { x: l.v, y:
|
|
2357
|
+
const hit = vertical ? { x: l.v, y: at2.y + dir.y * s } : { x: at2.x + dir.x * s, y: l.v };
|
|
1216
2358
|
const moved = distance(hit, q);
|
|
1217
2359
|
if (moved <= reach && (!ray || s > 0) && (!best || moved < best.moved)) best = { moved, point: hit, from: l.from };
|
|
1218
2360
|
}
|
|
@@ -1228,16 +2370,16 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1228
2370
|
/** Target segments whose control box reaches `p` and which pass within `tol` of it, nearest first. */
|
|
1229
2371
|
segmentsNear(p, tol) {
|
|
1230
2372
|
const out = [];
|
|
1231
|
-
const
|
|
2373
|
+
const pt2 = toPoint(p);
|
|
1232
2374
|
for (const s of this.segments) {
|
|
1233
2375
|
if (p.x < s.minX - tol || p.x > s.maxX + tol || p.y < s.minY - tol || p.y > s.maxY + tol) continue;
|
|
1234
|
-
const hit =
|
|
2376
|
+
const hit = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, s.seg, pt2);
|
|
1235
2377
|
if (hit.distance <= tol) out.push({ seg: s.seg, t: hit.t, distance: hit.distance });
|
|
1236
2378
|
}
|
|
1237
2379
|
return out.sort((a, b) => a.distance - b.distance);
|
|
1238
2380
|
}
|
|
1239
2381
|
onSegment(near2) {
|
|
1240
|
-
return near2.length ?
|
|
2382
|
+
return near2.length ? toVec2(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
|
|
1241
2383
|
}
|
|
1242
2384
|
/** Where two of the target segments near `p` cross, the crossing nearest `p` within `tol`. */
|
|
1243
2385
|
crossing(near2, p, tol) {
|
|
@@ -1246,8 +2388,8 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1246
2388
|
let bestD = Infinity;
|
|
1247
2389
|
for (let i = 0; i < list.length; i++) {
|
|
1248
2390
|
for (let j = i + 1; j < list.length; j++) {
|
|
1249
|
-
for (const hit of
|
|
1250
|
-
const q =
|
|
2391
|
+
for (const hit of _chunkGVB7ZPLZcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
|
|
2392
|
+
const q = toVec2(hit.point);
|
|
1251
2393
|
const d = distance(q, p);
|
|
1252
2394
|
if (d <= tol && d < bestD) {
|
|
1253
2395
|
best = q;
|
|
@@ -1285,15 +2427,15 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1285
2427
|
* with nothing spaces the box evenly in its row or column. Returns the correction and guides.
|
|
1286
2428
|
*/
|
|
1287
2429
|
snapRect(r, tol, options = {}) {
|
|
1288
|
-
const axes = { x: _optionalChain([options, 'access',
|
|
2430
|
+
const axes = { x: _optionalChain([options, 'access', _52 => _52.axes, 'optionalAccess', _53 => _53.x]) !== false, y: _optionalChain([options, 'access', _54 => _54.axes, 'optionalAccess', _55 => _55.y]) !== false };
|
|
1289
2431
|
const nearest = (lines, values) => {
|
|
1290
2432
|
let best = null;
|
|
1291
|
-
for (const
|
|
2433
|
+
for (const v2 of values) for (const l of linesNear(lines, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.d)) best = { d: l.v - v2, from: l.from };
|
|
1292
2434
|
return best;
|
|
1293
2435
|
};
|
|
1294
2436
|
const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
|
|
1295
2437
|
const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
|
|
1296
|
-
const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess',
|
|
2438
|
+
const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _56 => _56.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _57 => _57.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
|
|
1297
2439
|
const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
|
|
1298
2440
|
if (bx) {
|
|
1299
2441
|
const x = bx.from.x;
|
|
@@ -1331,7 +2473,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1331
2473
|
evenSpacing(r, horizontal, tol) {
|
|
1332
2474
|
const span = (b, x) => x ? [b.x, b.x + b.width] : [b.y, b.y + b.height];
|
|
1333
2475
|
const [r0, r1] = span(r, horizontal);
|
|
1334
|
-
const
|
|
2476
|
+
const len2 = r1 - r0;
|
|
1335
2477
|
const [c0, c1] = span(r, !horizontal);
|
|
1336
2478
|
const row = this.boxes.map((b) => ({ b, a0: span(b, horizontal)[0], a1: span(b, horizontal)[1] })).filter((e) => span(e.b, !horizontal)[0] < c1 && span(e.b, !horizontal)[1] > c0 && (e.a1 <= r0 + tol || e.a0 >= r1 - tol)).sort((a, b) => a.a0 - b.a0);
|
|
1337
2479
|
if (!row.length) return null;
|
|
@@ -1351,20 +2493,20 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1351
2493
|
if (e.a0 >= r1 - tol && (!after || e.a0 < after.a0)) after = e;
|
|
1352
2494
|
}
|
|
1353
2495
|
const starts = [];
|
|
1354
|
-
if (before && after && after.a0 - before.a1 >
|
|
2496
|
+
if (before && after && after.a0 - before.a1 > len2) starts.push({ start: (before.a1 + after.a0 - len2) / 2, repeats: null });
|
|
1355
2497
|
for (const { g } of pairs) {
|
|
1356
2498
|
if (before) starts.push({ start: before.a1 + g, repeats: g });
|
|
1357
|
-
if (after) starts.push({ start: after.a0 - g -
|
|
2499
|
+
if (after) starts.push({ start: after.a0 - g - len2, repeats: g });
|
|
1358
2500
|
}
|
|
1359
2501
|
let pick = null;
|
|
1360
2502
|
for (const s of starts) if (Math.abs(s.start - r0) <= tol && (!pick || Math.abs(s.start - r0) < Math.abs(pick.start - r0))) pick = s;
|
|
1361
2503
|
if (!pick) return null;
|
|
1362
2504
|
const { start, repeats } = pick;
|
|
1363
|
-
const
|
|
1364
|
-
const end = start +
|
|
2505
|
+
const at2 = horizontal ? { ...r, x: start } : { ...r, y: start };
|
|
2506
|
+
const end = start + len2;
|
|
1365
2507
|
const gaps = [];
|
|
1366
|
-
if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b,
|
|
1367
|
-
if (after && (repeats === null || Math.abs(after.a0 - end - repeats) <= EQUAL)) gaps.push([end, after.a0, mid(after.b,
|
|
2508
|
+
if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b, at2)]);
|
|
2509
|
+
if (after && (repeats === null || Math.abs(after.a0 - end - repeats) <= EQUAL)) gaps.push([end, after.a0, mid(after.b, at2)]);
|
|
1368
2510
|
if (repeats !== null) {
|
|
1369
2511
|
for (const p of pairs) if (Math.abs(p.g - repeats) <= EQUAL) gaps.push(p.gap);
|
|
1370
2512
|
}
|
|
@@ -1372,9 +2514,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1372
2514
|
}
|
|
1373
2515
|
/** A gap mark: a line across the gap with a tick at each end, and its length as a label. */
|
|
1374
2516
|
gapMarks([u0, u1, c], horizontal, tick, out) {
|
|
1375
|
-
const
|
|
1376
|
-
out.lines.push(line("spacing",
|
|
1377
|
-
out.labels.push(this.label(
|
|
2517
|
+
const pt2 = (u, v2) => horizontal ? { x: u, y: v2 } : { x: v2, y: u };
|
|
2518
|
+
out.lines.push(line("spacing", pt2(u0, c), pt2(u1, c)), line("spacing", pt2(u0, c - tick), pt2(u0, c + tick)), line("spacing", pt2(u1, c - tick), pt2(u1, c + tick)));
|
|
2519
|
+
out.labels.push(this.label(pt2((u0 + u1) / 2, c), "spacing", fmtLength(u1 - u0)));
|
|
1378
2520
|
}
|
|
1379
2521
|
/** The other objects' side length nearest `length`, within `tol`, or null. */
|
|
1380
2522
|
matchSize(length2, tol) {
|
|
@@ -1401,9 +2543,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1401
2543
|
const hp = handlePos(handle, frame);
|
|
1402
2544
|
const d0 = { x: hp.x - origin.x, y: hp.y - origin.y };
|
|
1403
2545
|
const valid = (n, s) => Number.isFinite(n) && n * s > 0;
|
|
1404
|
-
const nearest = (
|
|
2546
|
+
const nearest = (v2, lines) => {
|
|
1405
2547
|
let best = null;
|
|
1406
|
-
for (const l of linesNear(lines,
|
|
2548
|
+
for (const l of linesNear(lines, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.v - v2)) best = l;
|
|
1407
2549
|
return best;
|
|
1408
2550
|
};
|
|
1409
2551
|
let nx = sx;
|
|
@@ -1416,9 +2558,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1416
2558
|
const on = this.nearestPoint(p, tol);
|
|
1417
2559
|
const tries = [];
|
|
1418
2560
|
if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
|
|
1419
|
-
tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess',
|
|
1420
|
-
tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess',
|
|
1421
|
-
tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess',
|
|
2561
|
+
tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _58 => _58.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _59 => _59.v]), () => ( null)), s: { hx, hy, on: null } });
|
|
2562
|
+
tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _60 => _60.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
|
|
2563
|
+
tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _61 => _61.v]), () => ( null)), s: { hx: null, hy, on: null } });
|
|
1422
2564
|
for (const { tx, ty, s } of tries) {
|
|
1423
2565
|
if (tx === null && ty === null) continue;
|
|
1424
2566
|
const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
|
|
@@ -1454,9 +2596,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1454
2596
|
};
|
|
1455
2597
|
for (const h of [handle, ...ends]) {
|
|
1456
2598
|
const hv = handlePos(h, frame);
|
|
1457
|
-
const
|
|
1458
|
-
const base = applyMatrix(toDoc, { x: origin.x + c.x *
|
|
1459
|
-
const dir = applyMatrix(toDoc, { x: e.x *
|
|
2599
|
+
const v2 = { x: hv.x - origin.x, y: hv.y - origin.y };
|
|
2600
|
+
const base = applyMatrix(toDoc, { x: origin.x + c.x * v2.x, y: origin.y + c.y * v2.y });
|
|
2601
|
+
const dir = applyMatrix(toDoc, { x: e.x * v2.x, y: e.y * v2.y });
|
|
1460
2602
|
const p = { x: base.x + dir.x * s0, y: base.y + dir.y * s0 };
|
|
1461
2603
|
const l2 = dir.x * dir.x + dir.y * dir.y;
|
|
1462
2604
|
const on = l2 > 1e-18 ? this.nearestPoint(p, tol) : null;
|
|
@@ -1489,16 +2631,16 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
1489
2631
|
const sized = [];
|
|
1490
2632
|
if (!proportional) {
|
|
1491
2633
|
if (movesX && !snap.hx && !(frame.angle && snap.hy) && frame.width > 1e-9) {
|
|
1492
|
-
const
|
|
1493
|
-
if (
|
|
1494
|
-
out.sx = Math.sign(out.sx || 1) * (
|
|
2634
|
+
const len2 = this.matchSize(Math.abs(out.sx) * frame.width, tol);
|
|
2635
|
+
if (len2 !== null && len2 > 0) {
|
|
2636
|
+
out.sx = Math.sign(out.sx || 1) * (len2 / frame.width);
|
|
1495
2637
|
sized.push("x");
|
|
1496
2638
|
}
|
|
1497
2639
|
}
|
|
1498
2640
|
if (movesY && !snap.hy && !(frame.angle && snap.hx) && frame.height > 1e-9) {
|
|
1499
|
-
const
|
|
1500
|
-
if (
|
|
1501
|
-
out.sy = Math.sign(out.sy || 1) * (
|
|
2641
|
+
const len2 = this.matchSize(Math.abs(out.sy) * frame.height, tol);
|
|
2642
|
+
if (len2 !== null && len2 > 0) {
|
|
2643
|
+
out.sy = Math.sign(out.sy || 1) * (len2 / frame.height);
|
|
1502
2644
|
sized.push("y");
|
|
1503
2645
|
}
|
|
1504
2646
|
}
|
|
@@ -1549,14 +2691,14 @@ function line(kind, a, b) {
|
|
|
1549
2691
|
return { kind, x1: a.x, y1: a.y, x2: b.x, y2: b.y };
|
|
1550
2692
|
}
|
|
1551
2693
|
function construction(leave, p, tol) {
|
|
1552
|
-
const
|
|
1553
|
-
if (Math.hypot(
|
|
2694
|
+
const v2 = { x: p.x - leave.at.x, y: p.y - leave.at.y };
|
|
2695
|
+
if (Math.hypot(v2.x, v2.y) <= tol) return null;
|
|
1554
2696
|
let best = null;
|
|
1555
2697
|
for (const a of _nullishCoalesce(leave.angles, () => ( []))) {
|
|
1556
2698
|
const r = a * Math.PI / 180;
|
|
1557
2699
|
const exact = (n) => Math.abs(n) < 1e-12 ? 0 : n;
|
|
1558
2700
|
const dir = { x: exact(Math.cos(r)), y: exact(-Math.sin(r)) };
|
|
1559
|
-
const k =
|
|
2701
|
+
const k = v2.x * dir.x + v2.y * dir.y;
|
|
1560
2702
|
const point = { x: leave.at.x + dir.x * k, y: leave.at.y + dir.y * k };
|
|
1561
2703
|
const d = distance(point, p);
|
|
1562
2704
|
if (d <= tol && (!best || d < best.d)) best = { dir, point, d };
|
|
@@ -1568,20 +2710,20 @@ var crossCondition = (r, d) => r[0] * d[1] - r[1] * d[0];
|
|
|
1568
2710
|
function solveOnSegment(seg, base, cond) {
|
|
1569
2711
|
const b = [base.x, base.y];
|
|
1570
2712
|
const g = (t) => {
|
|
1571
|
-
const p =
|
|
1572
|
-
return cond([p[0] - b[0], p[1] - b[1]],
|
|
2713
|
+
const p = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
|
|
2714
|
+
return cond([p[0] - b[0], p[1] - b[1]], _chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, seg, t));
|
|
1573
2715
|
};
|
|
1574
2716
|
const vals = [];
|
|
1575
2717
|
let scale = 0;
|
|
1576
2718
|
for (let i = 0; i <= CURVE_SAMPLES; i++) {
|
|
1577
2719
|
const t = i / CURVE_SAMPLES;
|
|
1578
|
-
const p =
|
|
1579
|
-
const d =
|
|
2720
|
+
const p = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
|
|
2721
|
+
const d = _chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, seg, t);
|
|
1580
2722
|
const r = [p[0] - b[0], p[1] - b[1]];
|
|
1581
2723
|
vals.push(cond(r, d));
|
|
1582
2724
|
scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
|
|
1583
2725
|
}
|
|
1584
|
-
if (!(scale > 0) || vals.every((
|
|
2726
|
+
if (!(scale > 0) || vals.every((v2) => Math.abs(v2) <= scale * 1e-12)) return [];
|
|
1585
2727
|
const out = [];
|
|
1586
2728
|
for (let i = 0; i < CURVE_SAMPLES; i++) {
|
|
1587
2729
|
const ga = vals[i];
|
|
@@ -1603,8 +2745,8 @@ function solveOnSegment(seg, base, cond) {
|
|
|
1603
2745
|
} else hi = m;
|
|
1604
2746
|
}
|
|
1605
2747
|
const t = (lo + hi) / 2;
|
|
1606
|
-
const p =
|
|
1607
|
-
const dv =
|
|
2748
|
+
const p = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
|
|
2749
|
+
const dv = _chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, seg, t);
|
|
1608
2750
|
const r = Math.hypot(p[0] - b[0], p[1] - b[1]);
|
|
1609
2751
|
const dl = Math.hypot(dv[0], dv[1]);
|
|
1610
2752
|
if (!(r > 1e-9 && dl > 0) || Math.abs(cond([p[0] - b[0], p[1] - b[1]], dv)) > 1e-6 * r * dl) continue;
|
|
@@ -1614,8 +2756,8 @@ function solveOnSegment(seg, base, cond) {
|
|
|
1614
2756
|
}
|
|
1615
2757
|
|
|
1616
2758
|
// src/vector/pathEdit.ts
|
|
1617
|
-
var
|
|
1618
|
-
var
|
|
2759
|
+
var EPS3 = 1e-9;
|
|
2760
|
+
var unit2 = (x, y) => {
|
|
1619
2761
|
const l = Math.hypot(x, y);
|
|
1620
2762
|
return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
|
|
1621
2763
|
};
|
|
@@ -1628,26 +2770,26 @@ function cubicOf(sp, i) {
|
|
|
1628
2770
|
var isLine = (sp, i) => subpathSegment(sp, i).length === 2;
|
|
1629
2771
|
function startTangent(c) {
|
|
1630
2772
|
for (let k = 1; k < 4; k++) {
|
|
1631
|
-
const u =
|
|
2773
|
+
const u = unit2(c[k][0] - c[0][0], c[k][1] - c[0][1]);
|
|
1632
2774
|
if (u) return u;
|
|
1633
2775
|
}
|
|
1634
2776
|
return [1, 0];
|
|
1635
2777
|
}
|
|
1636
2778
|
function endTangent(c) {
|
|
1637
2779
|
for (let k = 2; k >= 0; k--) {
|
|
1638
|
-
const u =
|
|
2780
|
+
const u = unit2(c[3][0] - c[k][0], c[3][1] - c[k][1]);
|
|
1639
2781
|
if (u) return u;
|
|
1640
2782
|
}
|
|
1641
2783
|
return [1, 0];
|
|
1642
2784
|
}
|
|
1643
2785
|
function sample(c, n, out, withStart) {
|
|
1644
|
-
for (let i = withStart ? 0 : 1; i <= n; i++) out.push(
|
|
2786
|
+
for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, c, i / n));
|
|
1645
2787
|
}
|
|
1646
|
-
var
|
|
2788
|
+
var onAnchor2 = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
|
|
1647
2789
|
function tidy(a, keepType = true) {
|
|
1648
2790
|
const next = cloneAnchor(a);
|
|
1649
|
-
if (
|
|
1650
|
-
if (
|
|
2791
|
+
if (onAnchor2(next, next.in)) delete next.in;
|
|
2792
|
+
if (onAnchor2(next, next.out)) delete next.out;
|
|
1651
2793
|
const shown = inferAnchorType(next);
|
|
1652
2794
|
const type = keepType && a.type ? a.type === "symmetric" && shown !== "symmetric" ? shown === "corner" ? "corner" : "smooth" : a.type === "smooth" && shown === "corner" ? "corner" : a.type : shown;
|
|
1653
2795
|
if (type === "corner") delete next.type;
|
|
@@ -1687,7 +2829,7 @@ function fitAcross(pts, N, t0, t1) {
|
|
|
1687
2829
|
const u = [];
|
|
1688
2830
|
for (let i = 0; i <= N; i++) u.push(k * i / N);
|
|
1689
2831
|
for (let i = 1; i <= N; i++) u.push(k + (1 - k) * i / N);
|
|
1690
|
-
return
|
|
2832
|
+
return _chunkGVB7ZPLZcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
|
|
1691
2833
|
};
|
|
1692
2834
|
return goldenMin(fit, 0, 1);
|
|
1693
2835
|
}
|
|
@@ -1747,7 +2889,7 @@ function bendSegment(sp, segment, t, p) {
|
|
|
1747
2889
|
return { anchors, closed: sp.closed };
|
|
1748
2890
|
}
|
|
1749
2891
|
function subpathPointAt(sp, segment, t) {
|
|
1750
|
-
const [x, y] =
|
|
2892
|
+
const [x, y] = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
|
|
1751
2893
|
return { x, y };
|
|
1752
2894
|
}
|
|
1753
2895
|
function curvatureMoveAnchor(sp, index, q) {
|
|
@@ -1755,8 +2897,8 @@ function curvatureMoveAnchor(sp, index, q) {
|
|
|
1755
2897
|
const a = sp.anchors[index];
|
|
1756
2898
|
if (!a) return { anchors, closed: sp.closed };
|
|
1757
2899
|
const own = [
|
|
1758
|
-
a.in && !
|
|
1759
|
-
a.out && !
|
|
2900
|
+
a.in && !onAnchor2(a, a.in) ? { x: a.in.x - a.x, y: a.in.y - a.y } : null,
|
|
2901
|
+
a.out && !onAnchor2(a, a.out) ? { x: a.out.x - a.x, y: a.out.y - a.y } : null
|
|
1760
2902
|
];
|
|
1761
2903
|
const ends = [null, null];
|
|
1762
2904
|
neighbours(sp, index).forEach((j, side) => {
|
|
@@ -1775,15 +2917,16 @@ function curvatureMoveAnchor(sp, index, q) {
|
|
|
1775
2917
|
if (own[0]) moved.in = { x: q.x + own[0].x, y: q.y + own[0].y };
|
|
1776
2918
|
if (own[1]) moved.out = { x: q.x + own[1].x, y: q.y + own[1].y };
|
|
1777
2919
|
if (a.type) moved.type = a.type;
|
|
2920
|
+
if (a.corner) moved.corner = { ...a.corner };
|
|
1778
2921
|
const [prev, next] = ends;
|
|
1779
2922
|
if (a.type && a.type !== "corner" && prev && next) {
|
|
1780
2923
|
const dx = next.x - prev.x;
|
|
1781
2924
|
const dy = next.y - prev.y;
|
|
1782
2925
|
const l = Math.hypot(dx, dy);
|
|
1783
2926
|
if (l > 1e-9) {
|
|
1784
|
-
const
|
|
1785
|
-
let li =
|
|
1786
|
-
let lo =
|
|
2927
|
+
const len2 = (h, to) => h ? Math.hypot(h.x, h.y) : Math.hypot(q.x - to.x, q.y - to.y) / 3;
|
|
2928
|
+
let li = len2(own[0], prev);
|
|
2929
|
+
let lo = len2(own[1], next);
|
|
1787
2930
|
if (a.type === "symmetric") li = lo = (li + lo) / 2;
|
|
1788
2931
|
moved.in = { x: q.x - dx * li / l, y: q.y - dy * li / l };
|
|
1789
2932
|
moved.out = { x: q.x + dx * lo / l, y: q.y + dy * lo / l };
|
|
@@ -1792,9 +2935,9 @@ function curvatureMoveAnchor(sp, index, q) {
|
|
|
1792
2935
|
anchors[index] = moved;
|
|
1793
2936
|
return { anchors, closed: sp.closed };
|
|
1794
2937
|
}
|
|
1795
|
-
function cutSubpathAt(sp,
|
|
2938
|
+
function cutSubpathAt(sp, at2) {
|
|
1796
2939
|
const n = sp.anchors.length;
|
|
1797
|
-
const cuts = [...new Set(
|
|
2940
|
+
const cuts = [...new Set(at2)].filter((i) => Number.isInteger(i) && i >= 0 && i < n && (sp.closed || i > 0 && i < n - 1)).sort((a, b) => a - b);
|
|
1798
2941
|
if (!cuts.length || n < 2) return [{ anchors: sp.anchors.map(cloneAnchor), closed: sp.closed }];
|
|
1799
2942
|
const first = cuts[0];
|
|
1800
2943
|
const run = sp.closed ? Array.from({ length: n + 1 }, (_, i) => sp.anchors[(first + i) % n]) : sp.anchors;
|
|
@@ -1824,8 +2967,8 @@ function splitSubpathAt(sp, segment, t, endTolerance = 1e-6) {
|
|
|
1824
2967
|
const { subpath, index } = insertAnchor(sp, segment, t);
|
|
1825
2968
|
return cutSubpathAt(subpath, [index]);
|
|
1826
2969
|
}
|
|
1827
|
-
var
|
|
1828
|
-
var
|
|
2970
|
+
var endOf2 = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
|
|
2971
|
+
var dist2 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
1829
2972
|
function reverse(sp) {
|
|
1830
2973
|
return {
|
|
1831
2974
|
closed: sp.closed,
|
|
@@ -1834,6 +2977,7 @@ function reverse(sp) {
|
|
|
1834
2977
|
if (a.out) next.in = { ...a.out };
|
|
1835
2978
|
if (a.in) next.out = { ...a.in };
|
|
1836
2979
|
if (a.type) next.type = a.type;
|
|
2980
|
+
if (a.corner) next.corner = { ...a.corner };
|
|
1837
2981
|
return next;
|
|
1838
2982
|
})
|
|
1839
2983
|
};
|
|
@@ -1851,7 +2995,7 @@ function joinSubpaths(subpaths, tolerance) {
|
|
|
1851
2995
|
const sp = open[0];
|
|
1852
2996
|
const first = sp.anchors[0];
|
|
1853
2997
|
const last = sp.anchors[sp.anchors.length - 1];
|
|
1854
|
-
if (sp.anchors.length > 2 &&
|
|
2998
|
+
if (sp.anchors.length > 2 && dist2(first, last) <= tolerance) {
|
|
1855
2999
|
sp.anchors.pop();
|
|
1856
3000
|
sp.anchors[0] = tidy({ ...first, in: _nullishCoalesce(last.in, () => ( null)) }, false);
|
|
1857
3001
|
} else {
|
|
@@ -1867,7 +3011,7 @@ function joinSubpaths(subpaths, tolerance) {
|
|
|
1867
3011
|
for (let j = i + 1; j < open.length; j++) {
|
|
1868
3012
|
for (const ei of [false, true]) {
|
|
1869
3013
|
for (const ej of [false, true]) {
|
|
1870
|
-
const d =
|
|
3014
|
+
const d = dist2(endOf2(open[i], ei), endOf2(open[j], ej));
|
|
1871
3015
|
if (d < best.d) best = { d, i, j, ei, ej };
|
|
1872
3016
|
}
|
|
1873
3017
|
}
|
|
@@ -1905,10 +3049,10 @@ function smoothSubpath(sp, amount = 1, only) {
|
|
|
1905
3049
|
const prev = orig[(i + n - 1) % n];
|
|
1906
3050
|
const next = orig[(i + 1) % n];
|
|
1907
3051
|
const p = orig[i];
|
|
1908
|
-
const dir =
|
|
3052
|
+
const dir = unit2(next.x - prev.x, next.y - prev.y);
|
|
1909
3053
|
if (!dir) continue;
|
|
1910
|
-
const li =
|
|
1911
|
-
const lo =
|
|
3054
|
+
const li = dist2(p, prev) / 3;
|
|
3055
|
+
const lo = dist2(p, next) / 3;
|
|
1912
3056
|
const hin = _nullishCoalesce(p.in, () => ( p));
|
|
1913
3057
|
const hout = _nullishCoalesce(p.out, () => ( p));
|
|
1914
3058
|
const target = (h, x, y) => ({ x: h.x + (x - h.x) * t, y: h.y + (y - h.y) * t });
|
|
@@ -1935,12 +3079,12 @@ function segsOf(sp) {
|
|
|
1935
3079
|
function segsToSubpath(segs, closed, types) {
|
|
1936
3080
|
if (!segs.length) return null;
|
|
1937
3081
|
const anchors = [];
|
|
1938
|
-
const
|
|
1939
|
-
anchors.push(
|
|
3082
|
+
const at2 = (p) => ({ x: p[0], y: p[1] });
|
|
3083
|
+
anchors.push(at2(segs[0].c[0]));
|
|
1940
3084
|
for (const s of segs) {
|
|
1941
3085
|
const end = s.c[s.c.length - 1];
|
|
1942
3086
|
if (!s.line) anchors[anchors.length - 1].out = { x: s.c[1][0], y: s.c[1][1] };
|
|
1943
|
-
const next =
|
|
3087
|
+
const next = at2(end);
|
|
1944
3088
|
if (!s.line) next.in = { x: s.c[2][0], y: s.c[2][1] };
|
|
1945
3089
|
anchors.push(next);
|
|
1946
3090
|
}
|
|
@@ -1962,12 +3106,12 @@ function mergePair(a, b, tol) {
|
|
|
1962
3106
|
if (a.line && b.line) {
|
|
1963
3107
|
const cx = b3[0] - a0[0];
|
|
1964
3108
|
const cy = b3[1] - a0[1];
|
|
1965
|
-
const
|
|
1966
|
-
if (
|
|
3109
|
+
const len2 = Math.hypot(cx, cy);
|
|
3110
|
+
if (len2 < 1e-12) return null;
|
|
1967
3111
|
const mx = a3[0] - a0[0];
|
|
1968
3112
|
const my = a3[1] - a0[1];
|
|
1969
|
-
const
|
|
1970
|
-
return Math.abs((mx * cy - my * cx) /
|
|
3113
|
+
const along2 = (mx * cx + my * cy) / len2;
|
|
3114
|
+
return Math.abs((mx * cy - my * cx) / len2) <= tol && along2 >= 0 && along2 <= len2 ? { c: [a0, b3], line: true } : null;
|
|
1971
3115
|
}
|
|
1972
3116
|
const ca = a.line ? [a0, a0, a3, a3] : a.c;
|
|
1973
3117
|
const cb = b.line ? [a3, a3, b3, b3] : b.c;
|
|
@@ -2006,12 +3150,12 @@ function removeRedundantPoints(sp, tolerance) {
|
|
|
2006
3150
|
}
|
|
2007
3151
|
|
|
2008
3152
|
// src/vector/pathCut.ts
|
|
2009
|
-
var loopArea = (l) =>
|
|
3153
|
+
var loopArea = (l) => _chunkGVB7ZPLZcjs.contourSignedArea.call(void 0, l.segs);
|
|
2010
3154
|
function loopSample(l) {
|
|
2011
3155
|
const k = l.chord.findIndex((c) => !c);
|
|
2012
|
-
return
|
|
3156
|
+
return _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
|
|
2013
3157
|
}
|
|
2014
|
-
var contains2 = (l, p) =>
|
|
3158
|
+
var contains2 = (l, p) => _chunkGVB7ZPLZcjs.windingNumber.call(void 0, [l.segs], p) !== 0;
|
|
2015
3159
|
function groupFaces(loops) {
|
|
2016
3160
|
const usable = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
|
|
2017
3161
|
if (!usable.length) return [];
|
|
@@ -2027,12 +3171,12 @@ function groupFaces(loops) {
|
|
|
2027
3171
|
return outers.reverse().map((o) => faces.get(o));
|
|
2028
3172
|
}
|
|
2029
3173
|
function faceToPath(face) {
|
|
2030
|
-
return
|
|
3174
|
+
return _chunkGVB7ZPLZcjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
|
|
2031
3175
|
}
|
|
2032
3176
|
function facesOf(path, fillRule) {
|
|
2033
|
-
const normalized =
|
|
2034
|
-
const loops =
|
|
2035
|
-
const segs =
|
|
3177
|
+
const normalized = _chunkGVB7ZPLZcjs.normalizeBezierPath.call(void 0, path, fillRule);
|
|
3178
|
+
const loops = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, normalized).map((c) => {
|
|
3179
|
+
const segs = _chunkGVB7ZPLZcjs.closeContour.call(void 0, c, true).map((s) => s.slice());
|
|
2036
3180
|
return { segs, chord: segs.map(() => false) };
|
|
2037
3181
|
});
|
|
2038
3182
|
return groupFaces(loops);
|
|
@@ -2091,7 +3235,7 @@ function lineHits(p, q, seg) {
|
|
|
2091
3235
|
const dy = q[1] - p[1];
|
|
2092
3236
|
const len2 = dx * dx + dy * dy;
|
|
2093
3237
|
if (len2 < 1e-24) return [];
|
|
2094
|
-
const h = (
|
|
3238
|
+
const h = (pt2) => (pt2[0] - p[0]) * -dy + (pt2[1] - p[1]) * dx;
|
|
2095
3239
|
let coeffs;
|
|
2096
3240
|
if (seg.length === 2) coeffs = [0, 0, h(seg[1]) - h(seg[0]), h(seg[0])];
|
|
2097
3241
|
else {
|
|
@@ -2099,10 +3243,10 @@ function lineHits(p, q, seg) {
|
|
|
2099
3243
|
coeffs = [-h0 + 3 * h1 - 3 * h2 + h3, 3 * h0 - 6 * h1 + 3 * h2, -3 * h0 + 3 * h1, h0];
|
|
2100
3244
|
}
|
|
2101
3245
|
if (coeffs.every((c) => Math.abs(c) < 1e-12 * Math.max(1, len2))) return [];
|
|
2102
|
-
const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((
|
|
3246
|
+
const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
|
|
2103
3247
|
const out = [];
|
|
2104
3248
|
for (const t of rootsInUnit(...coeffs, scale)) {
|
|
2105
|
-
const point =
|
|
3249
|
+
const point = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
|
|
2106
3250
|
const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
|
|
2107
3251
|
if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
|
|
2108
3252
|
}
|
|
@@ -2115,7 +3259,7 @@ function arc(l, a, b, eps) {
|
|
|
2115
3259
|
const segs = [];
|
|
2116
3260
|
const chord = [];
|
|
2117
3261
|
const push = (k, t0, t1) => {
|
|
2118
|
-
const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() :
|
|
3262
|
+
const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : _chunkGVB7ZPLZcjs.subSegment.call(void 0, l.segs[k], t0, t1);
|
|
2119
3263
|
if (!degenerate(piece, eps)) {
|
|
2120
3264
|
segs.push(piece);
|
|
2121
3265
|
chord.push(l.chord[k]);
|
|
@@ -2140,7 +3284,7 @@ function knifePath(path, fillRule, knife) {
|
|
|
2140
3284
|
if (pts.length < 2) return null;
|
|
2141
3285
|
let faces = facesOf(path, fillRule);
|
|
2142
3286
|
if (!faces.length) return null;
|
|
2143
|
-
const b =
|
|
3287
|
+
const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, path);
|
|
2144
3288
|
const size = b ? Math.max(b.maxX - b.minX, b.maxY - b.minY, 1) : 1;
|
|
2145
3289
|
const eps = size * 1e-9;
|
|
2146
3290
|
let cursor = -Infinity;
|
|
@@ -2154,7 +3298,7 @@ function knifePath(path, fillRule, knife) {
|
|
|
2154
3298
|
const kb = { minX: Math.min(pts[m][0], pts[m + 1][0]), maxX: Math.max(pts[m][0], pts[m + 1][0]), minY: Math.min(pts[m][1], pts[m + 1][1]), maxY: Math.max(pts[m][1], pts[m + 1][1]) };
|
|
2155
3299
|
face2.loops.forEach(
|
|
2156
3300
|
(l, li) => l.segs.forEach((seg, si) => {
|
|
2157
|
-
const sb =
|
|
3301
|
+
const sb = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, seg);
|
|
2158
3302
|
if (sb.maxX < kb.minX - eps || sb.minX > kb.maxX + eps || sb.maxY < kb.minY - eps || sb.minY > kb.maxY + eps) return;
|
|
2159
3303
|
for (const h of lineHits(pts[m], pts[m + 1], seg)) {
|
|
2160
3304
|
const s = m + h.s;
|
|
@@ -2169,7 +3313,7 @@ function knifePath(path, fillRule, knife) {
|
|
|
2169
3313
|
const m = Math.min(pts.length - 2, Math.floor(s));
|
|
2170
3314
|
const u = s - m;
|
|
2171
3315
|
const q = [pts[m][0] + (pts[m + 1][0] - pts[m][0]) * u, pts[m][1] + (pts[m + 1][1] - pts[m][1]) * u];
|
|
2172
|
-
return
|
|
3316
|
+
return _chunkGVB7ZPLZcjs.windingNumber.call(void 0, face2.loops.map((l) => l.segs), q) !== 0;
|
|
2173
3317
|
};
|
|
2174
3318
|
for (let i = 0; i < unique.length - 1; i++) {
|
|
2175
3319
|
const from2 = unique[i];
|
|
@@ -2229,33 +3373,33 @@ function eraserRegion(points, width) {
|
|
|
2229
3373
|
];
|
|
2230
3374
|
}
|
|
2231
3375
|
const line2 = [{ type: "M", x: pts[0][0], y: pts[0][1] }, ...pts.slice(1).map((p) => ({ type: "L", x: p[0], y: p[1] }))];
|
|
2232
|
-
return
|
|
3376
|
+
return _chunkGVB7ZPLZcjs.outlineStroke.call(void 0, line2, { width, cap: "round", join: "round" });
|
|
2233
3377
|
}
|
|
2234
3378
|
function eraseFromFill(path, fillRule, region) {
|
|
2235
|
-
const overlap =
|
|
2236
|
-
if (!
|
|
2237
|
-
const rest =
|
|
3379
|
+
const overlap = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
|
|
3380
|
+
if (!_chunkGVB7ZPLZcjs.pathToContours.call(void 0, overlap).some((c) => Math.abs(_chunkGVB7ZPLZcjs.contourSignedArea.call(void 0, _chunkGVB7ZPLZcjs.closeContour.call(void 0, c, true))) > 1e-9)) return null;
|
|
3381
|
+
const rest = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "difference", [{ path, fillRule }, { path: region }]).path;
|
|
2238
3382
|
return splitIntoFaces(rest);
|
|
2239
3383
|
}
|
|
2240
3384
|
function eraseFromOutline(sp, region) {
|
|
2241
|
-
const regionSegs =
|
|
3385
|
+
const regionSegs = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, region).flatMap((c) => _chunkGVB7ZPLZcjs.closeContour.call(void 0, c, true));
|
|
2242
3386
|
if (!regionSegs.length) return null;
|
|
2243
|
-
const rb =
|
|
3387
|
+
const rb = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, region);
|
|
2244
3388
|
const count = subpathSegmentCount(sp);
|
|
2245
3389
|
if (!rb || count === 0) return null;
|
|
2246
|
-
const removed = (p) =>
|
|
3390
|
+
const removed = (p) => _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
|
|
2247
3391
|
const runs = [];
|
|
2248
3392
|
let cur = [];
|
|
2249
3393
|
let any = false;
|
|
2250
3394
|
for (let i = 0; i < count; i++) {
|
|
2251
3395
|
const seg = subpathSegment(sp, i);
|
|
2252
|
-
const sb =
|
|
3396
|
+
const sb = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, seg);
|
|
2253
3397
|
const ts = [0, 1];
|
|
2254
3398
|
if (!(sb.maxX < rb.minX || sb.minX > rb.maxX || sb.maxY < rb.minY || sb.minY > rb.maxY)) {
|
|
2255
3399
|
for (const r of regionSegs) {
|
|
2256
|
-
const b =
|
|
3400
|
+
const b = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, r);
|
|
2257
3401
|
if (b.maxX < sb.minX || b.minX > sb.maxX || b.maxY < sb.minY || b.minY > sb.maxY) continue;
|
|
2258
|
-
for (const hit of
|
|
3402
|
+
for (const hit of _chunkGVB7ZPLZcjs.segmentIntersections.call(void 0, seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
|
|
2259
3403
|
}
|
|
2260
3404
|
}
|
|
2261
3405
|
ts.sort((a, b) => a - b);
|
|
@@ -2263,7 +3407,7 @@ function eraseFromOutline(sp, region) {
|
|
|
2263
3407
|
const t0 = ts[k];
|
|
2264
3408
|
const t1 = ts[k + 1];
|
|
2265
3409
|
if (t1 - t0 < 1e-9) continue;
|
|
2266
|
-
if (removed(
|
|
3410
|
+
if (removed(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
|
|
2267
3411
|
any = true;
|
|
2268
3412
|
if (cur.length) runs.push(cur);
|
|
2269
3413
|
cur = [];
|
|
@@ -2282,7 +3426,7 @@ function eraseFromOutline(sp, region) {
|
|
|
2282
3426
|
for (const run of runs) {
|
|
2283
3427
|
const anchors = [];
|
|
2284
3428
|
for (const { seg, t0, t1 } of run) {
|
|
2285
|
-
const c =
|
|
3429
|
+
const c = _chunkGVB7ZPLZcjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
|
|
2286
3430
|
const line2 = c.length === 2;
|
|
2287
3431
|
const start = c[0];
|
|
2288
3432
|
const end = c[c.length - 1];
|
|
@@ -2326,12 +3470,12 @@ function simplifyPolyline(points, tolerance) {
|
|
|
2326
3470
|
const [ax, ay] = points[a];
|
|
2327
3471
|
const dx = points[b][0] - ax;
|
|
2328
3472
|
const dy = points[b][1] - ay;
|
|
2329
|
-
const
|
|
3473
|
+
const len2 = Math.hypot(dx, dy);
|
|
2330
3474
|
let far = -1;
|
|
2331
3475
|
let farD = 0;
|
|
2332
3476
|
for (let i = a + 1; i < b; i++) {
|
|
2333
3477
|
const [px, py] = points[i];
|
|
2334
|
-
const d =
|
|
3478
|
+
const d = len2 < 1e-12 ? Math.hypot(px - ax, py - ay) : Math.abs((px - ax) * dy - (py - ay) * dx) / len2;
|
|
2335
3479
|
if (d > farD) {
|
|
2336
3480
|
farD = d;
|
|
2337
3481
|
far = i;
|
|
@@ -2399,7 +3543,7 @@ function vectorInkShapeName(shape) {
|
|
|
2399
3543
|
return shape.points.length === 3 ? "triangle" : "polygon";
|
|
2400
3544
|
}
|
|
2401
3545
|
}
|
|
2402
|
-
var
|
|
3546
|
+
var dist3 = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
2403
3547
|
function bbox(pts) {
|
|
2404
3548
|
if (!pts.length) return [0, 0, 0, 0];
|
|
2405
3549
|
let [x0, y0] = pts[0];
|
|
@@ -2417,7 +3561,7 @@ function segDistance(a, b, p) {
|
|
|
2417
3561
|
const dy = b[1] - a[1];
|
|
2418
3562
|
const len2 = dx * dx + dy * dy;
|
|
2419
3563
|
const t = len2 > 0 ? Math.min(1, Math.max(0, ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / len2)) : 0;
|
|
2420
|
-
return
|
|
3564
|
+
return dist3([a[0] + t * dx, a[1] + t * dy], p);
|
|
2421
3565
|
}
|
|
2422
3566
|
function polygonDistance(c, p) {
|
|
2423
3567
|
let best = Infinity;
|
|
@@ -2443,11 +3587,11 @@ function snapSquare(x, y, w, h) {
|
|
|
2443
3587
|
}
|
|
2444
3588
|
function turning(a, b, c) {
|
|
2445
3589
|
const u = [b[0] - a[0], b[1] - a[1]];
|
|
2446
|
-
const
|
|
3590
|
+
const v2 = [c[0] - b[0], c[1] - b[1]];
|
|
2447
3591
|
const lu = Math.hypot(u[0], u[1]);
|
|
2448
|
-
const lv = Math.hypot(
|
|
3592
|
+
const lv = Math.hypot(v2[0], v2[1]);
|
|
2449
3593
|
if (lu <= 0 || lv <= 0) return 0;
|
|
2450
|
-
return Math.acos(Math.min(1, Math.max(-1, (u[0] *
|
|
3594
|
+
return Math.acos(Math.min(1, Math.max(-1, (u[0] * v2[0] + u[1] * v2[1]) / (lu * lv)))) * 180 / Math.PI;
|
|
2451
3595
|
}
|
|
2452
3596
|
function rdp(p, eps) {
|
|
2453
3597
|
const n = p.length;
|
|
@@ -2479,7 +3623,7 @@ function corners(p, diag) {
|
|
|
2479
3623
|
let far = 0;
|
|
2480
3624
|
let farD = 0;
|
|
2481
3625
|
p.forEach((q, i) => {
|
|
2482
|
-
const d =
|
|
3626
|
+
const d = dist3(start, q);
|
|
2483
3627
|
if (d > farD) {
|
|
2484
3628
|
farD = d;
|
|
2485
3629
|
far = i;
|
|
@@ -2506,7 +3650,7 @@ function corners(p, diag) {
|
|
|
2506
3650
|
}
|
|
2507
3651
|
let short = -1;
|
|
2508
3652
|
for (let j = 0; j < n; j++) {
|
|
2509
|
-
if (
|
|
3653
|
+
if (dist3(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
|
|
2510
3654
|
short = j;
|
|
2511
3655
|
break;
|
|
2512
3656
|
}
|
|
@@ -2546,8 +3690,8 @@ function recognizeInkShape(points, options = {}) {
|
|
|
2546
3690
|
const diag = Math.hypot(w, h);
|
|
2547
3691
|
if (size < minSize) return null;
|
|
2548
3692
|
let length2 = 0;
|
|
2549
|
-
for (let i = 1; i < p.length; i++) length2 +=
|
|
2550
|
-
const gap =
|
|
3693
|
+
for (let i = 1; i < p.length; i++) length2 += dist3(p[i - 1], p[i]);
|
|
3694
|
+
const gap = dist3(first, last);
|
|
2551
3695
|
if (gap > 0.85 * length2 && gap >= minSize) {
|
|
2552
3696
|
const off = Math.max(...p.map((q) => segDistance(first, last, q)));
|
|
2553
3697
|
if (off <= Math.max(0.06 * gap, 1.5 * minSize / 6)) return { kind: "line", a: first, b: last };
|
|
@@ -2573,8 +3717,31 @@ function recognizeInkShape(points, options = {}) {
|
|
|
2573
3717
|
|
|
2574
3718
|
// src/vector/controller.ts
|
|
2575
3719
|
var DEFAULT_VECTOR_SNAP = { grid: 0, objects: true, guides: true, threshold: 5, angles: VECTOR_CONSTRUCTION_ANGLES };
|
|
2576
|
-
var DEFAULT_VECTOR_PATH_EDIT = {
|
|
3720
|
+
var DEFAULT_VECTOR_PATH_EDIT = {
|
|
3721
|
+
keepShapeOnDelete: true,
|
|
3722
|
+
curvatureMoves: false,
|
|
3723
|
+
pencilSmoothing: 0,
|
|
3724
|
+
recognizeShapes: false,
|
|
3725
|
+
eraserSize: 10,
|
|
3726
|
+
pencilPressure: "pen"
|
|
3727
|
+
};
|
|
2577
3728
|
var VECTOR_PENCIL_STRING_PX = 100;
|
|
3729
|
+
var VECTOR_STYLE_KEYS = [
|
|
3730
|
+
"fill",
|
|
3731
|
+
"stroke",
|
|
3732
|
+
"strokeWidth",
|
|
3733
|
+
"strokeLinecap",
|
|
3734
|
+
"strokeLinejoin",
|
|
3735
|
+
"strokeDasharray",
|
|
3736
|
+
"fillRule",
|
|
3737
|
+
"strokeAlign",
|
|
3738
|
+
"startArrow",
|
|
3739
|
+
"endArrow",
|
|
3740
|
+
"arrowAlign",
|
|
3741
|
+
"startArrowScale",
|
|
3742
|
+
"endArrowScale",
|
|
3743
|
+
"strokeWidthProfile"
|
|
3744
|
+
];
|
|
2578
3745
|
var VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
|
|
2579
3746
|
var DEFAULT_DEFAULTS = {
|
|
2580
3747
|
fill: "#d4d4d8",
|
|
@@ -2602,7 +3769,7 @@ function getEditablePathIds(state) {
|
|
|
2602
3769
|
}
|
|
2603
3770
|
}
|
|
2604
3771
|
}
|
|
2605
|
-
return [...ids].filter((id) => _optionalChain([findVectorNode, 'call',
|
|
3772
|
+
return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _62 => _62(state.document.nodes, id), 'optionalAccess', _63 => _63.type]) === "path");
|
|
2606
3773
|
}
|
|
2607
3774
|
function getVisibleAnchors(state) {
|
|
2608
3775
|
const out = [];
|
|
@@ -2617,7 +3784,7 @@ function getVisibleHandles(state) {
|
|
|
2617
3784
|
const seen = /* @__PURE__ */ new Set();
|
|
2618
3785
|
const push = (node, s, i, handle) => {
|
|
2619
3786
|
const sp = node.subpaths[s];
|
|
2620
|
-
const a = _optionalChain([sp, 'optionalAccess',
|
|
3787
|
+
const a = _optionalChain([sp, 'optionalAccess', _64 => _64.anchors, 'access', _65 => _65[i]]);
|
|
2621
3788
|
if (!a || !a[handle]) return;
|
|
2622
3789
|
const key = `${node.id}:${s}:${i}:${handle}`;
|
|
2623
3790
|
if (seen.has(key)) return;
|
|
@@ -2626,8 +3793,8 @@ function getVisibleHandles(state) {
|
|
|
2626
3793
|
};
|
|
2627
3794
|
if (state.tool === "pen" && state.pen) {
|
|
2628
3795
|
const node = findVectorNode(state.document.nodes, state.pen.nodeId);
|
|
2629
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
2630
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
3796
|
+
const sp = _optionalChain([node, 'optionalAccess', _66 => _66.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
|
|
3797
|
+
if (_optionalChain([node, 'optionalAccess', _67 => _67.type]) === "path" && sp) {
|
|
2631
3798
|
const last = sp.anchors.length - 1;
|
|
2632
3799
|
push(node, state.pen.subpath, last, "in");
|
|
2633
3800
|
push(node, state.pen.subpath, last, "out");
|
|
@@ -2635,9 +3802,9 @@ function getVisibleHandles(state) {
|
|
|
2635
3802
|
return out;
|
|
2636
3803
|
}
|
|
2637
3804
|
if (state.tool !== "direct") return out;
|
|
2638
|
-
const
|
|
3805
|
+
const editable2 = new Set(getEditablePathIds(state));
|
|
2639
3806
|
for (const ref of state.selectedAnchors) {
|
|
2640
|
-
if (!
|
|
3807
|
+
if (!editable2.has(ref.nodeId)) continue;
|
|
2641
3808
|
const node = findVectorNode(state.document.nodes, ref.nodeId);
|
|
2642
3809
|
const sp = node.subpaths[ref.subpath];
|
|
2643
3810
|
if (!sp) continue;
|
|
@@ -2651,9 +3818,82 @@ function getVisibleHandles(state) {
|
|
|
2651
3818
|
}
|
|
2652
3819
|
return out;
|
|
2653
3820
|
}
|
|
3821
|
+
var VECTOR_CORNER_WIDGET_INSET = 10;
|
|
3822
|
+
function getVectorCornerWidgets(state) {
|
|
3823
|
+
if (state.tool !== "direct" || state.pen) return [];
|
|
3824
|
+
const zoom = state.viewport.zoom;
|
|
3825
|
+
const out = [];
|
|
3826
|
+
for (const id of getEditablePathIds(state)) {
|
|
3827
|
+
const node = findVectorNode(state.document.nodes, id);
|
|
3828
|
+
const picked = state.selectedAnchors.filter((r) => r.nodeId === id);
|
|
3829
|
+
const m = _nullishCoalesce(vectorWorldMatrix(state.document.nodes, id), () => ( IDENTITY_MATRIX));
|
|
3830
|
+
for (const c of vectorCorners(node.subpaths)) {
|
|
3831
|
+
if (picked.length && !picked.some((r) => r.subpath === c.subpath && r.index === c.index)) continue;
|
|
3832
|
+
const side = Math.min(c.lu, c.lv);
|
|
3833
|
+
if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
|
|
3834
|
+
const a = node.subpaths[c.subpath].anchors[c.index];
|
|
3835
|
+
const radius = _nullishCoalesce(_optionalChain([a, 'access', _68 => _68.corner, 'optionalAccess', _69 => _69.radius]), () => ( 0));
|
|
3836
|
+
const drawn = fitCornerRadius(c, radius);
|
|
3837
|
+
const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
|
|
3838
|
+
const p = applyMatrix(m, cornerBisectorPoint(c, k));
|
|
3839
|
+
out.push({ ref: { nodeId: id, subpath: c.subpath, index: c.index }, x: p.x, y: p.y, radius, drawnRadius: drawn, kind: _nullishCoalesce(_optionalChain([a, 'access', _70 => _70.corner, 'optionalAccess', _71 => _71.kind]), () => ( "round")), corner: c });
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
return out;
|
|
3843
|
+
}
|
|
3844
|
+
function vectorStrokeWidthGeometry(node) {
|
|
3845
|
+
if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return null;
|
|
3846
|
+
const parts = vectorStrokeParts(subpathsToBezier(node.subpaths), node);
|
|
3847
|
+
return parts.line.length ? { line: parts.line, unit: parts.drawWidth / 2 } : null;
|
|
3848
|
+
}
|
|
3849
|
+
function getVectorWidthHandles(state) {
|
|
3850
|
+
if (state.tool !== "width") return [];
|
|
3851
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3852
|
+
for (const id of state.selection) {
|
|
3853
|
+
const n = findVectorNode(state.document.nodes, id);
|
|
3854
|
+
if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
|
|
3855
|
+
}
|
|
3856
|
+
if (_optionalChain([state, 'access', _72 => _72.hover, 'optionalAccess', _73 => _73.nodeId])) ids.add(state.hover.nodeId);
|
|
3857
|
+
if (_optionalChain([state, 'access', _74 => _74.interaction, 'optionalAccess', _75 => _75.type]) === "width") ids.add(state.interaction.ref.nodeId);
|
|
3858
|
+
if (state.selectedWidthPoint) ids.add(state.selectedWidthPoint.nodeId);
|
|
3859
|
+
const out = [];
|
|
3860
|
+
for (const id of ids) {
|
|
3861
|
+
const node = findVectorNode(state.document.nodes, id);
|
|
3862
|
+
if (_optionalChain([node, 'optionalAccess', _76 => _76.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _77 => _77.strokeWidthProfile, 'optionalAccess', _78 => _78.length])) continue;
|
|
3863
|
+
const g = vectorStrokeWidthGeometry(node);
|
|
3864
|
+
if (!g) continue;
|
|
3865
|
+
const profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
3866
|
+
const m = _nullishCoalesce(vectorWorldMatrix(state.document.nodes, id), () => ( IDENTITY_MATRIX));
|
|
3867
|
+
const contours = node.subpaths.length;
|
|
3868
|
+
for (let sp = 0; sp < contours; sp++) {
|
|
3869
|
+
profile.forEach((w, index) => {
|
|
3870
|
+
const at2 = strokePositionPoint(g.line, sp, w.t);
|
|
3871
|
+
if (!at2) return;
|
|
3872
|
+
const n = { x: at2.tangent[1], y: -at2.tangent[0] };
|
|
3873
|
+
const c = { x: at2.point[0], y: at2.point[1] };
|
|
3874
|
+
const p = applyMatrix(m, c);
|
|
3875
|
+
const l = applyMatrix(m, { x: c.x + n.x * w.left * g.unit, y: c.y + n.y * w.left * g.unit });
|
|
3876
|
+
const r = applyMatrix(m, { x: c.x - n.x * w.right * g.unit, y: c.y - n.y * w.right * g.unit });
|
|
3877
|
+
const sel = state.selectedWidthPoint;
|
|
3878
|
+
out.push({
|
|
3879
|
+
ref: { nodeId: id, index, subpath: sp },
|
|
3880
|
+
x: p.x,
|
|
3881
|
+
y: p.y,
|
|
3882
|
+
left: l,
|
|
3883
|
+
right: r,
|
|
3884
|
+
t: w.t,
|
|
3885
|
+
leftScale: w.left,
|
|
3886
|
+
rightScale: w.right,
|
|
3887
|
+
selected: !!sel && sel.nodeId === id && sel.index === index
|
|
3888
|
+
});
|
|
3889
|
+
});
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
return out;
|
|
3893
|
+
}
|
|
2654
3894
|
function getAnchor(doc, ref) {
|
|
2655
3895
|
const node = findVectorNode(doc.nodes, ref.nodeId);
|
|
2656
|
-
return _optionalChain([node, 'optionalAccess',
|
|
3896
|
+
return _optionalChain([node, 'optionalAccess', _79 => _79.type]) === "path" ? _optionalChain([node, 'access', _80 => _80.subpaths, 'access', _81 => _81[ref.subpath], 'optionalAccess', _82 => _82.anchors, 'access', _83 => _83[ref.index]]) : void 0;
|
|
2657
3897
|
}
|
|
2658
3898
|
function getAnchorWorld(doc, ref) {
|
|
2659
3899
|
const a = getAnchor(doc, ref);
|
|
@@ -2721,9 +3961,10 @@ var TOOL_LABELS = {
|
|
|
2721
3961
|
line: "Line",
|
|
2722
3962
|
scissors: "Scissors",
|
|
2723
3963
|
knife: "Knife",
|
|
2724
|
-
eraser: "Eraser"
|
|
3964
|
+
eraser: "Eraser",
|
|
3965
|
+
width: "Width"
|
|
2725
3966
|
};
|
|
2726
|
-
var TOOL_KEYS = { v: "select", a: "direct", p: "pen", n: "pencil", r: "rect", o: "ellipse", y: "polygon", s: "star", l: "line", c: "scissors", k: "knife", e: "eraser" };
|
|
3967
|
+
var TOOL_KEYS = { v: "select", a: "direct", p: "pen", n: "pencil", r: "rect", o: "ellipse", y: "polygon", s: "star", l: "line", c: "scissors", k: "knife", e: "eraser", w: "width" };
|
|
2727
3968
|
function vectorToolLabel(tool) {
|
|
2728
3969
|
return TOOL_LABELS[tool];
|
|
2729
3970
|
}
|
|
@@ -2739,7 +3980,8 @@ var VECTOR_TOOL_SHORTCUTS = {
|
|
|
2739
3980
|
line: "L",
|
|
2740
3981
|
scissors: "C",
|
|
2741
3982
|
knife: "K",
|
|
2742
|
-
eraser: "E"
|
|
3983
|
+
eraser: "E",
|
|
3984
|
+
width: "W"
|
|
2743
3985
|
};
|
|
2744
3986
|
var VECTOR_TOOL_GLYPHS = {
|
|
2745
3987
|
select: "\u2196",
|
|
@@ -2753,7 +3995,8 @@ var VECTOR_TOOL_GLYPHS = {
|
|
|
2753
3995
|
line: "\u2571",
|
|
2754
3996
|
scissors: "\u2702",
|
|
2755
3997
|
knife: "\u27CB",
|
|
2756
|
-
eraser: "\u232B"
|
|
3998
|
+
eraser: "\u232B",
|
|
3999
|
+
width: "\u21F9"
|
|
2757
4000
|
};
|
|
2758
4001
|
var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
|
|
2759
4002
|
var BOOLEAN_OPS = {
|
|
@@ -2807,35 +4050,36 @@ var VectorEditorController = (_class3 = class {
|
|
|
2807
4050
|
|
|
2808
4051
|
__init9() {this.selection = []}
|
|
2809
4052
|
__init10() {this.anchors = []}
|
|
2810
|
-
__init11() {this.
|
|
2811
|
-
__init12() {this.
|
|
4053
|
+
__init11() {this.widthPoint = null}
|
|
4054
|
+
__init12() {this.editingId = null}
|
|
4055
|
+
__init13() {this.pen = null}
|
|
2812
4056
|
|
|
2813
4057
|
|
|
2814
4058
|
|
|
2815
4059
|
|
|
2816
4060
|
|
|
2817
4061
|
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
4062
|
+
__init14() {this.hover = null}
|
|
4063
|
+
__init15() {this.pointer = null}
|
|
4064
|
+
__init16() {this.drag = null}
|
|
4065
|
+
__init17() {this.interaction = null}
|
|
4066
|
+
__init18() {this.guides = null}
|
|
2823
4067
|
/** Whether the last `snapDrawn` landed on an object (guides may be hidden). */
|
|
2824
|
-
|
|
4068
|
+
__init19() {this.snappedToObject = false}
|
|
2825
4069
|
/** Drawing targets for hovering the pen and shape tools, kept while the document and the excluded path stay the same. */
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
4070
|
+
__init20() {this.hoverTargets = null}
|
|
4071
|
+
__init21() {this.spaceHeld = false}
|
|
4072
|
+
__init22() {this.clipboard = null}
|
|
2829
4073
|
|
|
2830
4074
|
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
4075
|
+
__init23() {this.listeners = /* @__PURE__ */ new Set()}
|
|
4076
|
+
__init24() {this.cleanups = []}
|
|
4077
|
+
__init25() {this.applying = false}
|
|
2834
4078
|
/** Set while a commit records a change that was already applied, so `restore` still reports it. */
|
|
2835
|
-
|
|
2836
|
-
|
|
4079
|
+
__init26() {this.committing = false}
|
|
4080
|
+
__init27() {this.revision = 0}
|
|
2837
4081
|
|
|
2838
|
-
constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);
|
|
4082
|
+
constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);_class3.prototype.__init29.call(this);
|
|
2839
4083
|
this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
|
|
2840
4084
|
this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
|
|
2841
4085
|
this.tool = _nullishCoalesce(options.tool, () => ( "select"));
|
|
@@ -2864,9 +4108,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
2864
4108
|
// State
|
|
2865
4109
|
// -------------------------------------------------------------------------
|
|
2866
4110
|
/** The current snapshot (stable until the next change). */
|
|
2867
|
-
|
|
4111
|
+
__init28() {this.getState = () => this.state}
|
|
2868
4112
|
/** Calls `listener` after every change. Returns an unsubscribe function. */
|
|
2869
|
-
|
|
4113
|
+
__init29() {this.subscribe = (listener) => {
|
|
2870
4114
|
this.listeners.add(listener);
|
|
2871
4115
|
return () => {
|
|
2872
4116
|
this.listeners.delete(listener);
|
|
@@ -2885,6 +4129,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
2885
4129
|
tool: this.tool,
|
|
2886
4130
|
selection: this.selection,
|
|
2887
4131
|
selectedAnchors: this.anchors,
|
|
4132
|
+
selectedWidthPoint: this.widthPoint,
|
|
2888
4133
|
editingId: this.editingId,
|
|
2889
4134
|
selectionBounds: this.boundsOf(this.selection),
|
|
2890
4135
|
selectionFrame: this.frameOf(this.selectedRoots()),
|
|
@@ -2909,7 +4154,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
2909
4154
|
notify(docChanged = false) {
|
|
2910
4155
|
this.revision++;
|
|
2911
4156
|
this.state = this.computeState();
|
|
2912
|
-
if (docChanged) _optionalChain([this, 'access',
|
|
4157
|
+
if (docChanged) _optionalChain([this, 'access', _84 => _84.onChange, 'optionalCall', _85 => _85(this.doc)]);
|
|
2913
4158
|
const state = this.state;
|
|
2914
4159
|
this.listeners.forEach((listener) => {
|
|
2915
4160
|
try {
|
|
@@ -2939,20 +4184,24 @@ var VectorEditorController = (_class3 = class {
|
|
|
2939
4184
|
return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
|
|
2940
4185
|
}
|
|
2941
4186
|
cursorFor() {
|
|
2942
|
-
if (_optionalChain([this, 'access',
|
|
2943
|
-
if (_optionalChain([this, 'access',
|
|
2944
|
-
if (_optionalChain([this, 'access',
|
|
4187
|
+
if (_optionalChain([this, 'access', _86 => _86.drag, 'optionalAccess', _87 => _87.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
|
|
4188
|
+
if (_optionalChain([this, 'access', _88 => _88.drag, 'optionalAccess', _89 => _89.kind]) === "rotate") return "grabbing";
|
|
4189
|
+
if (_optionalChain([this, 'access', _90 => _90.drag, 'optionalAccess', _91 => _91.kind]) === "move") return "move";
|
|
2945
4190
|
const h = this.hover;
|
|
2946
4191
|
switch (this.tool) {
|
|
2947
4192
|
case "select":
|
|
2948
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
2949
|
-
return _optionalChain([h, 'optionalAccess',
|
|
4193
|
+
if (_optionalChain([h, 'optionalAccess', _92 => _92.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _93 => _93.frameOf, 'call', _94 => _94(this.selectedRoots()), 'optionalAccess', _95 => _95.angle]), () => ( 0)));
|
|
4194
|
+
return _optionalChain([h, 'optionalAccess', _96 => _96.nodeId]) ? "move" : "default";
|
|
2950
4195
|
case "direct":
|
|
2951
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
2952
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
2953
|
-
return _optionalChain([h, 'optionalAccess',
|
|
4196
|
+
if (_optionalChain([h, 'optionalAccess', _97 => _97.anchor]) || _optionalChain([h, 'optionalAccess', _98 => _98.handle]) || _optionalChain([h, 'optionalAccess', _99 => _99.corner])) return "pointer";
|
|
4197
|
+
if (_optionalChain([h, 'optionalAccess', _100 => _100.segment])) return "copy";
|
|
4198
|
+
return _optionalChain([h, 'optionalAccess', _101 => _101.nodeId]) ? "move" : "default";
|
|
2954
4199
|
case "pen":
|
|
2955
|
-
return _optionalChain([h, 'optionalAccess',
|
|
4200
|
+
return _optionalChain([h, 'optionalAccess', _102 => _102.anchor]) ? "pointer" : "crosshair";
|
|
4201
|
+
case "width":
|
|
4202
|
+
if (_optionalChain([this, 'access', _103 => _103.drag, 'optionalAccess', _104 => _104.kind]) === "width") return this.drag.mode === "move" ? "grabbing" : "ew-resize";
|
|
4203
|
+
if (_optionalChain([h, 'optionalAccess', _105 => _105.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
|
|
4204
|
+
return _optionalChain([h, 'optionalAccess', _106 => _106.strokeSpot]) ? "copy" : "default";
|
|
2956
4205
|
default:
|
|
2957
4206
|
return "crosshair";
|
|
2958
4207
|
}
|
|
@@ -2972,7 +4221,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
2972
4221
|
this.selection = s.selection.filter((id) => this.getNode(id));
|
|
2973
4222
|
this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
|
|
2974
4223
|
this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
|
|
2975
|
-
this.pen = s.pen && _optionalChain([this, 'access',
|
|
4224
|
+
this.pen = s.pen && _optionalChain([this, 'access', _107 => _107.getNode, 'call', _108 => _108(s.pen.nodeId), 'optionalAccess', _109 => _109.subpaths, 'optionalAccess', _110 => _110[s.pen.subpath]]) ? s.pen : null;
|
|
2976
4225
|
} finally {
|
|
2977
4226
|
this.applying = false;
|
|
2978
4227
|
}
|
|
@@ -3033,12 +4282,12 @@ var VectorEditorController = (_class3 = class {
|
|
|
3033
4282
|
// Viewport
|
|
3034
4283
|
// -------------------------------------------------------------------------
|
|
3035
4284
|
screenToDocument(p) {
|
|
3036
|
-
const
|
|
3037
|
-
return { x: (p.x -
|
|
4285
|
+
const v2 = this.viewport;
|
|
4286
|
+
return { x: (p.x - v2.x) / v2.zoom, y: (p.y - v2.y) / v2.zoom };
|
|
3038
4287
|
}
|
|
3039
4288
|
documentToScreen(p) {
|
|
3040
|
-
const
|
|
3041
|
-
return { x: p.x *
|
|
4289
|
+
const v2 = this.viewport;
|
|
4290
|
+
return { x: p.x * v2.zoom + v2.x, y: p.y * v2.zoom + v2.y };
|
|
3042
4291
|
}
|
|
3043
4292
|
setViewport(viewport) {
|
|
3044
4293
|
const next = { ...this.viewport, ...viewport };
|
|
@@ -3078,6 +4327,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3078
4327
|
this.tool = tool;
|
|
3079
4328
|
this.hover = null;
|
|
3080
4329
|
this.guides = null;
|
|
4330
|
+
this.widthPoint = null;
|
|
3081
4331
|
if (tool !== "direct") {
|
|
3082
4332
|
this.anchors = [];
|
|
3083
4333
|
this.editingId = null;
|
|
@@ -3105,9 +4355,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
3105
4355
|
toggleSnapping(on = !this.snap.objects) {
|
|
3106
4356
|
this.setSnap({ objects: on });
|
|
3107
4357
|
}
|
|
3108
|
-
gridValue(
|
|
4358
|
+
gridValue(v2) {
|
|
3109
4359
|
const g = this.snap.grid;
|
|
3110
|
-
return g > 0 ? Math.round(
|
|
4360
|
+
return g > 0 ? Math.round(v2 / g) * g : v2;
|
|
3111
4361
|
}
|
|
3112
4362
|
snapPoint(p) {
|
|
3113
4363
|
return { x: this.gridValue(p.x), y: this.gridValue(p.y) };
|
|
@@ -3136,7 +4386,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3136
4386
|
if (res.kind) {
|
|
3137
4387
|
this.snappedToObject = true;
|
|
3138
4388
|
this.setGuides(res);
|
|
3139
|
-
return _optionalChain([leave, 'optionalAccess',
|
|
4389
|
+
return _optionalChain([leave, 'optionalAccess', _111 => _111.constrain]) ? res.point : { x: res.x ? res.point.x : this.gridValue(res.point.x), y: res.y ? res.point.y : this.gridValue(res.point.y) };
|
|
3140
4390
|
}
|
|
3141
4391
|
}
|
|
3142
4392
|
this.setGuides(null);
|
|
@@ -3154,14 +4404,14 @@ var VectorEditorController = (_class3 = class {
|
|
|
3154
4404
|
/** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
|
|
3155
4405
|
hoverSnap(p, input) {
|
|
3156
4406
|
if (this.tool === "pen") {
|
|
3157
|
-
const sp = this.pen ? _optionalChain([this, 'access',
|
|
3158
|
-
if (this.pen && sp && _optionalChain([this, 'access',
|
|
4407
|
+
const sp = this.pen ? _optionalChain([this, 'access', _112 => _112.getNode, 'call', _113 => _113(this.pen.nodeId), 'optionalAccess', _114 => _114.subpaths, 'access', _115 => _115[this.pen.subpath]]) : void 0;
|
|
4408
|
+
if (this.pen && sp && _optionalChain([this, 'access', _116 => _116.hover, 'optionalAccess', _117 => _117.anchor])) {
|
|
3159
4409
|
this.setGuides(null);
|
|
3160
4410
|
return p;
|
|
3161
4411
|
}
|
|
3162
4412
|
const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
|
|
3163
4413
|
const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
|
|
3164
|
-
return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access',
|
|
4414
|
+
return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _118 => _118.pen, 'optionalAccess', _119 => _119.nodeId]), () => ( null))));
|
|
3165
4415
|
}
|
|
3166
4416
|
if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
|
|
3167
4417
|
this.setGuides(null);
|
|
@@ -3263,7 +4513,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3263
4513
|
let subpath;
|
|
3264
4514
|
switch (input.kind) {
|
|
3265
4515
|
case "rect":
|
|
3266
|
-
subpath = rectSubpath(b
|
|
4516
|
+
subpath = rectSubpath(b);
|
|
3267
4517
|
break;
|
|
3268
4518
|
case "ellipse":
|
|
3269
4519
|
subpath = ellipseSubpath(b);
|
|
@@ -3278,10 +4528,13 @@ var VectorEditorController = (_class3 = class {
|
|
|
3278
4528
|
subpath = lineSubpath({ x: b.x, y: b.y }, { x: b.x + b.width, y: b.y + b.height });
|
|
3279
4529
|
break;
|
|
3280
4530
|
}
|
|
4531
|
+
const radius = _nullishCoalesce(input.cornerRadius, () => ( d.cornerRadius));
|
|
4532
|
+
const kind = _nullishCoalesce(input.cornerKind, () => ( d.cornerKind));
|
|
4533
|
+
if ((input.kind === "rect" || input.kind === "polygon" || input.kind === "star") && radius > 0) subpath = setSubpathCorners(subpath, radius, kind);
|
|
3281
4534
|
const names = { rect: "Rectangle", ellipse: "Ellipse", polygon: "Polygon", star: "Star", line: "Line" };
|
|
3282
4535
|
const style = this.styleFor(input.kind === "line");
|
|
3283
4536
|
const own = {};
|
|
3284
|
-
for (const key of
|
|
4537
|
+
for (const key of VECTOR_STYLE_KEYS) {
|
|
3285
4538
|
if (input[key] !== void 0) own[key] = input[key];
|
|
3286
4539
|
}
|
|
3287
4540
|
return { id: this.newId(), type: "path", name: _nullishCoalesce(input.name, () => ( names[input.kind])), ...style, ...own, subpaths: [subpath] };
|
|
@@ -3304,6 +4557,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
3304
4557
|
if (d.strokeLinejoin) out.strokeLinejoin = d.strokeLinejoin;
|
|
3305
4558
|
if (d.strokeDasharray) out.strokeDasharray = d.strokeDasharray;
|
|
3306
4559
|
if (d.fillRule) out.fillRule = d.fillRule;
|
|
4560
|
+
if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
|
|
4561
|
+
for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
|
|
4562
|
+
if (_optionalChain([d, 'access', _120 => _120.strokeWidthProfile, 'optionalAccess', _121 => _121.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
|
|
3307
4563
|
return out;
|
|
3308
4564
|
}
|
|
3309
4565
|
/** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
|
|
@@ -3324,10 +4580,129 @@ var VectorEditorController = (_class3 = class {
|
|
|
3324
4580
|
}
|
|
3325
4581
|
if (!targets.size) return { ok: false, message: "Nothing is selected." };
|
|
3326
4582
|
this.change(_nullishCoalesce(options.label, () => ( "Change Style")), () => {
|
|
3327
|
-
this.setDoc(mapTree(this.doc.nodes, (n) => targets.has(n.id) ?
|
|
4583
|
+
this.setDoc(mapTree(this.doc.nodes, (n) => targets.has(n.id) ? withStyle(n, style) : n));
|
|
4584
|
+
});
|
|
4585
|
+
return { ok: true };
|
|
4586
|
+
}
|
|
4587
|
+
/**
|
|
4588
|
+
* Sets the variable-width profile of the selected (or given) paths: a preset id
|
|
4589
|
+
* (`uniform` removes the profile), points, or a parametric taper / wave. Returns an error for
|
|
4590
|
+
* an unknown preset.
|
|
4591
|
+
*/
|
|
4592
|
+
setWidthProfile(profile, options = {}) {
|
|
4593
|
+
let points;
|
|
4594
|
+
if (profile === null) points = [];
|
|
4595
|
+
else if (typeof profile === "string") {
|
|
4596
|
+
const preset = widthProfilePreset(profile);
|
|
4597
|
+
if (!preset) return { ok: false, message: `There is no width profile "${profile}".` };
|
|
4598
|
+
points = preset;
|
|
4599
|
+
} else if (Array.isArray(profile)) points = sanitizeWidthProfile(profile);
|
|
4600
|
+
else {
|
|
4601
|
+
const p = profile;
|
|
4602
|
+
points = p.taper || p.wave ? generateWidthProfile(p) : [];
|
|
4603
|
+
}
|
|
4604
|
+
this.widthPoint = null;
|
|
4605
|
+
return this.setStyle({ strokeWidthProfile: points.length ? points : null }, _nullishCoalesce(options.ids, () => ( this.selection)), { label: points.length ? "Width Profile" : "Uniform Width" });
|
|
4606
|
+
}
|
|
4607
|
+
/** Removes one width point of a path's profile (the width tool's Delete). */
|
|
4608
|
+
removeWidthPoint(ref) {
|
|
4609
|
+
const node = this.getNode(ref.nodeId);
|
|
4610
|
+
if (_optionalChain([node, 'optionalAccess', _122 => _122.type]) !== "path" || !_optionalChain([node, 'access', _123 => _123.strokeWidthProfile, 'optionalAccess', _124 => _124[ref.index]])) return { ok: false, message: "There is no such width point." };
|
|
4611
|
+
const rest = removeWidthPoints(sanitizeWidthProfile(node.strokeWidthProfile), [ref.index]);
|
|
4612
|
+
this.widthPoint = null;
|
|
4613
|
+
this.change("Delete Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: rest.length ? rest : null })));
|
|
4614
|
+
return { ok: true };
|
|
4615
|
+
}
|
|
4616
|
+
/**
|
|
4617
|
+
* Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
|
|
4618
|
+
* anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
|
|
4619
|
+
* corners sharp again. Corners shrink to fit when drawn, so any radius is accepted.
|
|
4620
|
+
*/
|
|
4621
|
+
setCornerRadius(radius, options = {}) {
|
|
4622
|
+
if (!Number.isFinite(radius) || radius < 0) return { ok: false, message: "The radius must be a number of at least 0." };
|
|
4623
|
+
const refs = this.cornerTargets(options);
|
|
4624
|
+
if (!refs.length) return { ok: false, message: "Nothing is selected." };
|
|
4625
|
+
this.change(radius > 0 ? "Corner Radius" : "Sharpen Corners", () => {
|
|
4626
|
+
this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, radius, options.kind));
|
|
4627
|
+
});
|
|
4628
|
+
return { ok: true };
|
|
4629
|
+
}
|
|
4630
|
+
/** Sets the live corner kind of the same anchors as `setCornerRadius`, keeping their radii. */
|
|
4631
|
+
setCornerKind(kind, options = {}) {
|
|
4632
|
+
const refs = this.cornerTargets(options);
|
|
4633
|
+
if (!refs.length) return { ok: false, message: "Nothing is selected." };
|
|
4634
|
+
this.change("Corner Kind", () => {
|
|
4635
|
+
this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _125 => _125.corner, 'optionalAccess', _126 => _126.radius]), () => ( 0)), kind));
|
|
3328
4636
|
});
|
|
3329
4637
|
return { ok: true };
|
|
3330
4638
|
}
|
|
4639
|
+
/** Anchors a corner change applies to (see `setCornerRadius`). */
|
|
4640
|
+
cornerTargets(options) {
|
|
4641
|
+
if (options.refs) return options.refs.filter((r) => getAnchor(this.doc, r));
|
|
4642
|
+
if (!options.ids && this.tool === "direct" && this.anchors.length) return this.anchors.slice();
|
|
4643
|
+
const out = [];
|
|
4644
|
+
for (const id of _nullishCoalesce(options.ids, () => ( this.selection))) {
|
|
4645
|
+
const node = this.getNode(id);
|
|
4646
|
+
if (!node) continue;
|
|
4647
|
+
for (const p of collectPathNodes(node)) {
|
|
4648
|
+
if (p.locked) continue;
|
|
4649
|
+
p.subpaths.forEach((sp, s) => sp.anchors.forEach((_, i) => out.push({ nodeId: p.id, subpath: s, index: i })));
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
return out;
|
|
4653
|
+
}
|
|
4654
|
+
/**
|
|
4655
|
+
* Outline Stroke: replaces each selected (or given) path's stroke with a filled path of the
|
|
4656
|
+
* area it paints (alignment, joins, caps, dashes and arrowheads included), placed above a copy
|
|
4657
|
+
* of the path that keeps the fill (left out when there is none). Returns the new paths' ids.
|
|
4658
|
+
*/
|
|
4659
|
+
outlineStroke(options = {}) {
|
|
4660
|
+
const roots = _nullishCoalesce(options.ids, () => ( this.selectedRoots()));
|
|
4661
|
+
const paths = [];
|
|
4662
|
+
for (const id of roots) {
|
|
4663
|
+
const node = this.getNode(id);
|
|
4664
|
+
if (node) {
|
|
4665
|
+
for (const p of collectPathNodes(node)) if (!p.locked && hasPaint(p.stroke) && (_nullishCoalesce(p.strokeWidth, () => ( 1))) > 0) paths.push(p);
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
if (!paths.length) return [];
|
|
4669
|
+
const created = [];
|
|
4670
|
+
const replace = /* @__PURE__ */ new Map();
|
|
4671
|
+
for (const p of paths) {
|
|
4672
|
+
const outline = bezierToSubpaths(vectorStrokeOutlinePath(subpathsToBezier(p.subpaths), p));
|
|
4673
|
+
if (!outline.length) continue;
|
|
4674
|
+
const {
|
|
4675
|
+
stroke,
|
|
4676
|
+
strokeWidth: _w,
|
|
4677
|
+
strokeLinecap: _c,
|
|
4678
|
+
strokeLinejoin: _j,
|
|
4679
|
+
strokeDasharray: _d,
|
|
4680
|
+
strokeAlign: _a,
|
|
4681
|
+
startArrow: _s,
|
|
4682
|
+
endArrow: _e,
|
|
4683
|
+
arrowAlign: _al,
|
|
4684
|
+
startArrowScale: _ss,
|
|
4685
|
+
endArrowScale: _es,
|
|
4686
|
+
strokeWidthProfile: _wp,
|
|
4687
|
+
...rest
|
|
4688
|
+
} = cloneVectorNode(p);
|
|
4689
|
+
const id = this.newId();
|
|
4690
|
+
const strokeNode = { ...rest, id, name: `${_nullishCoalesce(p.name, () => ( "Path"))} Stroke`, subpaths: outline, fill: _nullishCoalesce(stroke, () => ( null)), stroke: null, fillRule: "nonzero" };
|
|
4691
|
+
const list = hasPaint(p.fill) ? [{ ...rest, stroke: null }, strokeNode] : [strokeNode];
|
|
4692
|
+
created.push(id);
|
|
4693
|
+
replace.set(p.id, list);
|
|
4694
|
+
}
|
|
4695
|
+
if (!created.length) return [];
|
|
4696
|
+
this.change("Outline Stroke", () => {
|
|
4697
|
+
let nodes = this.doc.nodes;
|
|
4698
|
+
for (const [id, list] of replace) nodes = spliceTree(nodes, id, list);
|
|
4699
|
+
this.setDoc(nodes);
|
|
4700
|
+
this.selection = created;
|
|
4701
|
+
this.anchors = [];
|
|
4702
|
+
this.editingId = null;
|
|
4703
|
+
});
|
|
4704
|
+
return created;
|
|
4705
|
+
}
|
|
3331
4706
|
rename(id, name) {
|
|
3332
4707
|
return this.updateNode(id, { name }, { label: "Rename" });
|
|
3333
4708
|
}
|
|
@@ -3396,10 +4771,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
3396
4771
|
return this.selection.filter((id) => {
|
|
3397
4772
|
let covered = false;
|
|
3398
4773
|
const loc = locateVectorNode(this.doc.nodes, id);
|
|
3399
|
-
let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess',
|
|
4774
|
+
let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _127 => _127.parent]), () => ( null));
|
|
3400
4775
|
while (parent) {
|
|
3401
4776
|
if (this.selection.includes(parent.id)) covered = true;
|
|
3402
|
-
parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call',
|
|
4777
|
+
parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _128 => _128(this.doc.nodes, parent.id), 'optionalAccess', _129 => _129.parent]), () => ( null));
|
|
3403
4778
|
}
|
|
3404
4779
|
return !covered;
|
|
3405
4780
|
});
|
|
@@ -3582,8 +4957,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
3582
4957
|
if (!ids.length) return null;
|
|
3583
4958
|
const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
|
|
3584
4959
|
if (locs.some((l) => !l)) return null;
|
|
3585
|
-
const parent = _nullishCoalesce(_optionalChain([locs, 'access',
|
|
3586
|
-
if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access',
|
|
4960
|
+
const parent = _nullishCoalesce(_optionalChain([locs, 'access', _130 => _130[0], 'access', _131 => _131.parent, 'optionalAccess', _132 => _132.id]), () => ( null));
|
|
4961
|
+
if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _133 => _133.parent, 'optionalAccess', _134 => _134.id]), () => ( null))) !== parent)) return null;
|
|
3587
4962
|
const id = this.newId();
|
|
3588
4963
|
this.change("Group", () => {
|
|
3589
4964
|
const set = new Set(ids);
|
|
@@ -3606,11 +4981,11 @@ var VectorEditorController = (_class3 = class {
|
|
|
3606
4981
|
}
|
|
3607
4982
|
/** Replaces selected groups with their children. */
|
|
3608
4983
|
ungroup(options = {}) {
|
|
3609
|
-
const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess',
|
|
4984
|
+
const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _135 => _135.type]) === "group");
|
|
3610
4985
|
if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
|
|
3611
4986
|
const set = new Set(groups.map((g) => g.id));
|
|
3612
4987
|
this.change("Ungroup", () => {
|
|
3613
|
-
const
|
|
4988
|
+
const flatten2 = (list) => {
|
|
3614
4989
|
const out = [];
|
|
3615
4990
|
let changed = false;
|
|
3616
4991
|
for (const n of list) {
|
|
@@ -3622,14 +4997,14 @@ var VectorEditorController = (_class3 = class {
|
|
|
3622
4997
|
...n.children.map((c) => n.transform ? transformVectorNode(c, m) : c).map((c) => opacity !== void 0 && opacity < 1 ? { ...c, opacity: (_nullishCoalesce(c.opacity, () => ( 1))) * opacity } : c)
|
|
3623
4998
|
);
|
|
3624
4999
|
} else if (n.type === "group") {
|
|
3625
|
-
const children =
|
|
5000
|
+
const children = flatten2(n.children);
|
|
3626
5001
|
if (children !== n.children) changed = true;
|
|
3627
5002
|
out.push(children !== n.children ? { ...n, children } : n);
|
|
3628
5003
|
} else out.push(n);
|
|
3629
5004
|
}
|
|
3630
5005
|
return changed ? out : list;
|
|
3631
5006
|
};
|
|
3632
|
-
this.setDoc(
|
|
5007
|
+
this.setDoc(flatten2(this.doc.nodes));
|
|
3633
5008
|
this.selection = groups.flatMap((g) => g.children.map((c) => c.id));
|
|
3634
5009
|
});
|
|
3635
5010
|
return { ok: true };
|
|
@@ -3666,7 +5041,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3666
5041
|
});
|
|
3667
5042
|
const paths = [...new Set(order)].map((id2) => this.getNode(id2));
|
|
3668
5043
|
if (paths.length < 2) return null;
|
|
3669
|
-
const result =
|
|
5044
|
+
const result = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0,
|
|
3670
5045
|
BOOLEAN_OPS[op],
|
|
3671
5046
|
paths.map((p) => ({
|
|
3672
5047
|
path: vectorPathWorldBezier(
|
|
@@ -3716,11 +5091,11 @@ var VectorEditorController = (_class3 = class {
|
|
|
3716
5091
|
/** A document-space path mapped into the parent space of node `id`. */
|
|
3717
5092
|
intoParentSpace(id, path) {
|
|
3718
5093
|
const parent = _nullishCoalesce(vectorParentMatrix(this.doc.nodes, id), () => ( IDENTITY_MATRIX));
|
|
3719
|
-
return isRoot(parent) ? path :
|
|
5094
|
+
return isRoot(parent) ? path : _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
|
|
3720
5095
|
}
|
|
3721
5096
|
/** Splits selected compound paths into one path per subpath. */
|
|
3722
5097
|
breakApart(options = {}) {
|
|
3723
|
-
const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess',
|
|
5098
|
+
const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _136 => _136.type]) === "path" && n.subpaths.length > 1);
|
|
3724
5099
|
if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
|
|
3725
5100
|
const created = [];
|
|
3726
5101
|
const set = new Set(paths.map((p) => p.id));
|
|
@@ -3782,7 +5157,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3782
5157
|
/** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
|
|
3783
5158
|
curvatureMoved(doc, ref, to) {
|
|
3784
5159
|
const node = findVectorNode(doc.nodes, ref.nodeId);
|
|
3785
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
5160
|
+
if (_optionalChain([node, 'optionalAccess', _137 => _137.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
|
|
3786
5161
|
const q = this.toLocal(ref.nodeId, to, doc);
|
|
3787
5162
|
const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
|
|
3788
5163
|
const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
|
|
@@ -3791,7 +5166,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3791
5166
|
/** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
|
|
3792
5167
|
bent(doc, ref, t, p) {
|
|
3793
5168
|
const node = findVectorNode(doc.nodes, ref.nodeId);
|
|
3794
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
5169
|
+
if (_optionalChain([node, 'optionalAccess', _138 => _138.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
|
|
3795
5170
|
const q = this.toLocal(ref.nodeId, p, doc);
|
|
3796
5171
|
const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
|
|
3797
5172
|
return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
|
|
@@ -3802,7 +5177,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3802
5177
|
*/
|
|
3803
5178
|
bendSegment(ref, to, t = 0.5) {
|
|
3804
5179
|
const node = this.getNode(ref.nodeId);
|
|
3805
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
5180
|
+
const sp = _optionalChain([node, 'optionalAccess', _139 => _139.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
|
|
3806
5181
|
if (!sp || ref.segment < 0 || ref.segment >= (sp.closed ? sp.anchors.length : sp.anchors.length - 1) || sp.anchors.length < 2) return { ok: false, message: "There is no such segment." };
|
|
3807
5182
|
if (node.locked) return { ok: false, message: "The path is locked." };
|
|
3808
5183
|
this.change("Bend Segment", () => {
|
|
@@ -3837,7 +5212,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3837
5212
|
/** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
|
|
3838
5213
|
insertAnchorAt(nodeId, point) {
|
|
3839
5214
|
const node = this.getNode(nodeId);
|
|
3840
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
5215
|
+
if (_optionalChain([node, 'optionalAccess', _140 => _140.type]) !== "path") return null;
|
|
3841
5216
|
const hit = nearestSegment(node, this.toLocal(nodeId, point));
|
|
3842
5217
|
if (!hit) return null;
|
|
3843
5218
|
const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
|
|
@@ -3881,7 +5256,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3881
5256
|
setClosed(closed, nodeId) {
|
|
3882
5257
|
const targets = nodeId ? (() => {
|
|
3883
5258
|
const n = this.getNode(nodeId);
|
|
3884
|
-
return _optionalChain([n, 'optionalAccess',
|
|
5259
|
+
return _optionalChain([n, 'optionalAccess', _141 => _141.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
|
|
3885
5260
|
})() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
|
|
3886
5261
|
if (!targets.length) return { ok: false, message: "Select a path or points." };
|
|
3887
5262
|
this.change(closed ? "Close Path" : "Open Path", () => {
|
|
@@ -3899,7 +5274,12 @@ var VectorEditorController = (_class3 = class {
|
|
|
3899
5274
|
const set = new Set(ids.flatMap((id) => this.getNode(id) ? collectPathNodes(this.getNode(id)) : []).map((p) => p.id));
|
|
3900
5275
|
if (!set.size) return { ok: false, message: "Select a path." };
|
|
3901
5276
|
this.change("Reverse Path", () => {
|
|
3902
|
-
this.setDoc(
|
|
5277
|
+
this.setDoc(
|
|
5278
|
+
mapTree(
|
|
5279
|
+
this.doc.nodes,
|
|
5280
|
+
(n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _142 => _142.strokeWidthProfile, 'optionalAccess', _143 => _143.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
|
|
5281
|
+
)
|
|
5282
|
+
);
|
|
3903
5283
|
this.anchors = [];
|
|
3904
5284
|
});
|
|
3905
5285
|
return { ok: true };
|
|
@@ -3930,7 +5310,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
3930
5310
|
const map = (p) => applyMatrix(m, p);
|
|
3931
5311
|
return from.subpaths.map((sp) => ({
|
|
3932
5312
|
closed: sp.closed,
|
|
3933
|
-
anchors: sp.anchors.map((a) => ({ ...map(a), ...a.in ? { in: map(a.in) } : {}, ...a.out ? { out: map(a.out) } : {}, ...a.type ? { type: a.type } : {} }))
|
|
5313
|
+
anchors: sp.anchors.map((a) => ({ ...map(a), ...a.in ? { in: map(a.in) } : {}, ...a.out ? { out: map(a.out) } : {}, ...a.type ? { type: a.type } : {}, ...a.corner ? { corner: { ...a.corner } } : {} }))
|
|
3934
5314
|
}));
|
|
3935
5315
|
}
|
|
3936
5316
|
/**
|
|
@@ -3980,9 +5360,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
3980
5360
|
const cx = points.reduce((s, p) => s + p.at.x, 0) / points.length;
|
|
3981
5361
|
const cy = points.reduce((s, p) => s + p.at.y, 0) / points.length;
|
|
3982
5362
|
this.change("Average", () => {
|
|
3983
|
-
for (const { ref, at } of points) {
|
|
3984
|
-
const dx = axis === "horizontal" ? 0 : cx -
|
|
3985
|
-
const dy = axis === "vertical" ? 0 : cy -
|
|
5363
|
+
for (const { ref, at: at2 } of points) {
|
|
5364
|
+
const dx = axis === "horizontal" ? 0 : cx - at2.x;
|
|
5365
|
+
const dy = axis === "vertical" ? 0 : cy - at2.y;
|
|
3986
5366
|
if (dx || dy) this.doc = this.movedAnchors(this.doc, [ref], dx, dy);
|
|
3987
5367
|
}
|
|
3988
5368
|
});
|
|
@@ -4035,7 +5415,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4035
5415
|
*/
|
|
4036
5416
|
cutPathAt(nodeId, point) {
|
|
4037
5417
|
const node = this.getNode(nodeId);
|
|
4038
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
5418
|
+
if (_optionalChain([node, 'optionalAccess', _144 => _144.type]) !== "path" || node.locked) return null;
|
|
4039
5419
|
const local = this.toLocal(nodeId, point);
|
|
4040
5420
|
const hit = nearestSegment(node, local);
|
|
4041
5421
|
if (!hit) return null;
|
|
@@ -4066,8 +5446,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4066
5446
|
const node = this.getNode(id);
|
|
4067
5447
|
if (node.locked) continue;
|
|
4068
5448
|
const ids = this.applyCuts(node, (sp, i) => {
|
|
4069
|
-
const
|
|
4070
|
-
const pieces =
|
|
5449
|
+
const at2 = list.filter((r) => r.subpath === i).map((r) => r.index);
|
|
5450
|
+
const pieces = at2.length ? cutSubpathAt(sp, at2) : null;
|
|
4071
5451
|
return pieces && !(pieces.length === 1 && pieces[0].closed === sp.closed) ? pieces : null;
|
|
4072
5452
|
});
|
|
4073
5453
|
if (ids) created.push(...ids);
|
|
@@ -4085,7 +5465,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4085
5465
|
if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
|
|
4086
5466
|
});
|
|
4087
5467
|
const visible = (p) => !blocked.has(p.id);
|
|
4088
|
-
if (_optionalChain([ids, 'optionalAccess',
|
|
5468
|
+
if (_optionalChain([ids, 'optionalAccess', _145 => _145.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
|
|
4089
5469
|
const box = polylineBounds(points.map(toPoint2), pad);
|
|
4090
5470
|
if (!box) return [];
|
|
4091
5471
|
const out = [];
|
|
@@ -4141,10 +5521,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
4141
5521
|
const region = eraserRegion(pts, width);
|
|
4142
5522
|
if (!region.length) return [];
|
|
4143
5523
|
const plan = [];
|
|
4144
|
-
const rb =
|
|
5524
|
+
const rb = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, region);
|
|
4145
5525
|
const reach = rb ? [{ x: rb.minX, y: rb.minY }, { x: rb.maxX, y: rb.maxY }] : points;
|
|
4146
5526
|
for (const node of this.strokeTargets(reach, options.ids, 0)) {
|
|
4147
|
-
const local =
|
|
5527
|
+
const local = _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
|
|
4148
5528
|
const live = node.subpaths.filter((sp) => sp.anchors.length > 1);
|
|
4149
5529
|
if (!live.length) continue;
|
|
4150
5530
|
if (hasPaint(node.fill) && live.every((sp) => sp.closed)) {
|
|
@@ -4171,7 +5551,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4171
5551
|
finishPath() {
|
|
4172
5552
|
if (!this.pen) return;
|
|
4173
5553
|
const node = this.getNode(this.pen.nodeId);
|
|
4174
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
5554
|
+
const sp = _optionalChain([node, 'optionalAccess', _146 => _146.subpaths, 'access', _147 => _147[this.pen.subpath]]);
|
|
4175
5555
|
const pen = this.pen;
|
|
4176
5556
|
this.pen = null;
|
|
4177
5557
|
this.pointer = null;
|
|
@@ -4215,19 +5595,19 @@ var VectorEditorController = (_class3 = class {
|
|
|
4215
5595
|
if (trimmed.startsWith("{")) {
|
|
4216
5596
|
try {
|
|
4217
5597
|
const data = JSON.parse(trimmed);
|
|
4218
|
-
if (_optionalChain([data, 'optionalAccess',
|
|
4219
|
-
} catch (
|
|
5598
|
+
if (_optionalChain([data, 'optionalAccess', _148 => _148.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
|
|
5599
|
+
} catch (e3) {
|
|
4220
5600
|
nodes = null;
|
|
4221
5601
|
}
|
|
4222
5602
|
} else if (/<svg[\s>]/i.test(trimmed)) {
|
|
4223
5603
|
try {
|
|
4224
5604
|
nodes = parseVectorSvg(trimmed).nodes;
|
|
4225
|
-
} catch (
|
|
5605
|
+
} catch (e4) {
|
|
4226
5606
|
nodes = null;
|
|
4227
5607
|
}
|
|
4228
5608
|
}
|
|
4229
5609
|
} else nodes = this.clipboard;
|
|
4230
|
-
if (!_optionalChain([nodes, 'optionalAccess',
|
|
5610
|
+
if (!_optionalChain([nodes, 'optionalAccess', _149 => _149.length])) return [];
|
|
4231
5611
|
const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
|
|
4232
5612
|
const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
|
|
4233
5613
|
this.change("Paste", () => {
|
|
@@ -4278,13 +5658,29 @@ var VectorEditorController = (_class3 = class {
|
|
|
4278
5658
|
hitHandle(screen) {
|
|
4279
5659
|
for (const ref of getVisibleHandles(this.view()).reverse()) {
|
|
4280
5660
|
const a = getAnchorWorld(this.doc, ref);
|
|
4281
|
-
const h = _optionalChain([a, 'optionalAccess',
|
|
5661
|
+
const h = _optionalChain([a, 'optionalAccess', _150 => _150[ref.handle]]);
|
|
4282
5662
|
if (!h) continue;
|
|
4283
5663
|
const s = this.documentToScreen(h);
|
|
4284
5664
|
if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
|
|
4285
5665
|
}
|
|
4286
5666
|
return null;
|
|
4287
5667
|
}
|
|
5668
|
+
cornerView() {
|
|
5669
|
+
return { ...this.view(), viewport: this.viewport };
|
|
5670
|
+
}
|
|
5671
|
+
hitCornerWidget(screen) {
|
|
5672
|
+
let best = null;
|
|
5673
|
+
let bestD = Infinity;
|
|
5674
|
+
for (const w of getVectorCornerWidgets(this.cornerView())) {
|
|
5675
|
+
const s = this.documentToScreen(w);
|
|
5676
|
+
const d = Math.hypot(s.x - screen.x, s.y - screen.y);
|
|
5677
|
+
if (d <= this.hitRadius && d < bestD) {
|
|
5678
|
+
best = w;
|
|
5679
|
+
bestD = d;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
return best;
|
|
5683
|
+
}
|
|
4288
5684
|
hitAnchor(screen, refs) {
|
|
4289
5685
|
let best = null;
|
|
4290
5686
|
let bestD = Infinity;
|
|
@@ -4343,6 +5739,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4343
5739
|
if (handle) return { nodeId: handle.nodeId, handle };
|
|
4344
5740
|
const anchor = this.hitAnchor(screen, getVisibleAnchors(this.view()));
|
|
4345
5741
|
if (anchor) return { nodeId: anchor.nodeId, anchor };
|
|
5742
|
+
const widget = this.hitCornerWidget(screen);
|
|
5743
|
+
if (widget) return { nodeId: widget.ref.nodeId, corner: widget.ref };
|
|
4346
5744
|
for (const id of getEditablePathIds(this.view())) {
|
|
4347
5745
|
const node = this.getNode(id);
|
|
4348
5746
|
const seg = nearestSegment(node, this.toLocal(id, p));
|
|
@@ -4363,10 +5761,131 @@ var VectorEditorController = (_class3 = class {
|
|
|
4363
5761
|
const seg = this.nearestOutline(p);
|
|
4364
5762
|
return seg ? { nodeId: seg.nodeId, segment: seg } : null;
|
|
4365
5763
|
}
|
|
5764
|
+
case "width": {
|
|
5765
|
+
const hit = this.hitWidthHandle(screen);
|
|
5766
|
+
if (hit) return { nodeId: hit.ref.nodeId, widthPoint: hit.ref };
|
|
5767
|
+
const spot = this.strokeSpotAt(p);
|
|
5768
|
+
return spot ? { nodeId: spot.nodeId, strokeSpot: spot } : null;
|
|
5769
|
+
}
|
|
4366
5770
|
default:
|
|
4367
5771
|
return null;
|
|
4368
5772
|
}
|
|
4369
5773
|
}
|
|
5774
|
+
widthView() {
|
|
5775
|
+
return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction, selectedWidthPoint: this.widthPoint };
|
|
5776
|
+
}
|
|
5777
|
+
/** The width point centre or side end under `screen` (width tool). */
|
|
5778
|
+
hitWidthHandle(screen) {
|
|
5779
|
+
let best = null;
|
|
5780
|
+
let bestD = Infinity;
|
|
5781
|
+
for (const h of getVectorWidthHandles(this.widthView())) {
|
|
5782
|
+
for (const [part, q] of [["center", h], ["left", h.left], ["right", h.right]]) {
|
|
5783
|
+
const s = this.documentToScreen(q);
|
|
5784
|
+
const d = Math.hypot(s.x - screen.x, s.y - screen.y);
|
|
5785
|
+
if (d <= this.hitRadius && d < bestD - 1e-9) {
|
|
5786
|
+
best = { ref: { ...h.ref, part } };
|
|
5787
|
+
bestD = d;
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
return best;
|
|
5792
|
+
}
|
|
5793
|
+
/** The nearest stroked, unlocked visible path whose stroke is under document point `p` (width tool). */
|
|
5794
|
+
strokeSpotAt(p) {
|
|
5795
|
+
let best = null;
|
|
5796
|
+
const tol = this.tolerance();
|
|
5797
|
+
walkVectorNodes(this.doc.nodes, (n) => {
|
|
5798
|
+
if (n.type !== "path" || n.visible === false || n.locked) return;
|
|
5799
|
+
if (hiddenOrLocked(this.doc.nodes, n.id)) return;
|
|
5800
|
+
const g = vectorStrokeWidthGeometry(n);
|
|
5801
|
+
if (!g) return;
|
|
5802
|
+
const m = this.worldOf(n.id);
|
|
5803
|
+
const local = applyMatrix(invertMatrix(m), p);
|
|
5804
|
+
const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
|
|
5805
|
+
if (!near2) return;
|
|
5806
|
+
const profile = strokeWidthProfileOf(n);
|
|
5807
|
+
const w = widthProfileAt(profile, near2.t);
|
|
5808
|
+
const reach = Math.max(near2.side === "left" ? w.left : w.right, 0) * g.unit + tol;
|
|
5809
|
+
if (near2.distance > reach || near2.distance > widthProfileMaxScale(profile) * g.unit + tol) return;
|
|
5810
|
+
if (best && near2.distance >= best.distance) return;
|
|
5811
|
+
const nx = near2.tangent[1];
|
|
5812
|
+
const ny = -near2.tangent[0];
|
|
5813
|
+
const [cx, cy] = near2.point;
|
|
5814
|
+
best = {
|
|
5815
|
+
nodeId: n.id,
|
|
5816
|
+
subpath: near2.contour,
|
|
5817
|
+
t: near2.t,
|
|
5818
|
+
point: applyMatrix(m, { x: cx, y: cy }),
|
|
5819
|
+
left: applyMatrix(m, { x: cx + nx * w.left * g.unit, y: cy + ny * w.left * g.unit }),
|
|
5820
|
+
right: applyMatrix(m, { x: cx - nx * w.right * g.unit, y: cy - ny * w.right * g.unit }),
|
|
5821
|
+
distance: near2.distance
|
|
5822
|
+
};
|
|
5823
|
+
});
|
|
5824
|
+
if (!best) return null;
|
|
5825
|
+
const { distance: _d, ...spot } = best;
|
|
5826
|
+
return spot;
|
|
5827
|
+
}
|
|
5828
|
+
downWidth(screen, p, input) {
|
|
5829
|
+
const hit = this.hitWidthHandle(screen);
|
|
5830
|
+
const spot = hit ? null : this.strokeSpotAt(p);
|
|
5831
|
+
const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _151 => _151.ref, 'access', _152 => _152.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _153 => _153.nodeId])));
|
|
5832
|
+
const node = nodeId ? this.getNode(nodeId) : void 0;
|
|
5833
|
+
const g = _optionalChain([node, 'optionalAccess', _154 => _154.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
|
|
5834
|
+
if (!node || node.type !== "path" || !g) {
|
|
5835
|
+
if (this.widthPoint) {
|
|
5836
|
+
this.widthPoint = null;
|
|
5837
|
+
this.notify();
|
|
5838
|
+
}
|
|
5839
|
+
return;
|
|
5840
|
+
}
|
|
5841
|
+
const before = this.snapshot();
|
|
5842
|
+
const local = this.toLocal(node.id, p);
|
|
5843
|
+
let profile;
|
|
5844
|
+
let ref;
|
|
5845
|
+
let mode = "width";
|
|
5846
|
+
let side = "both";
|
|
5847
|
+
let t;
|
|
5848
|
+
let subpath;
|
|
5849
|
+
if (hit) {
|
|
5850
|
+
profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
5851
|
+
ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
|
|
5852
|
+
t = _nullishCoalesce(_optionalChain([profile, 'access', _155 => _155[ref.index], 'optionalAccess', _156 => _156.t]), () => ( 0));
|
|
5853
|
+
subpath = hit.ref.subpath;
|
|
5854
|
+
if (hit.ref.part === "center") mode = "move";
|
|
5855
|
+
else if (input.alt) side = hit.ref.part;
|
|
5856
|
+
} else {
|
|
5857
|
+
const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
|
|
5858
|
+
const inserted = insertWidthPoint(strokeWidthProfileOf(node), near2.t);
|
|
5859
|
+
profile = inserted.points;
|
|
5860
|
+
subpath = near2.contour;
|
|
5861
|
+
t = near2.t;
|
|
5862
|
+
ref = { nodeId: node.id, index: inserted.index, subpath };
|
|
5863
|
+
if (input.alt) side = near2.side;
|
|
5864
|
+
}
|
|
5865
|
+
const at2 = strokePositionPoint(g.line, subpath, t);
|
|
5866
|
+
if (!at2) return;
|
|
5867
|
+
const base = { ...this.doc, nodes: mapTree(this.doc.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
|
|
5868
|
+
this.doc = base;
|
|
5869
|
+
this.widthPoint = { nodeId: node.id, index: ref.index, subpath };
|
|
5870
|
+
this.drag = {
|
|
5871
|
+
kind: "width",
|
|
5872
|
+
ref,
|
|
5873
|
+
mode,
|
|
5874
|
+
side,
|
|
5875
|
+
origin: { x: at2.point[0], y: at2.point[1] },
|
|
5876
|
+
normal: { x: at2.tangent[1], y: -at2.tangent[0] },
|
|
5877
|
+
unit: g.unit,
|
|
5878
|
+
profile,
|
|
5879
|
+
before,
|
|
5880
|
+
base,
|
|
5881
|
+
start: p,
|
|
5882
|
+
moved: false,
|
|
5883
|
+
created: !hit
|
|
5884
|
+
};
|
|
5885
|
+
const w = profile[ref.index];
|
|
5886
|
+
this.interaction = { type: "width", ref, mode, width: (w.left + w.right) * g.unit };
|
|
5887
|
+
this.notify(!hit);
|
|
5888
|
+
}
|
|
4370
5889
|
/** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
|
|
4371
5890
|
nearestOutline(p) {
|
|
4372
5891
|
let best = null;
|
|
@@ -4414,7 +5933,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
4414
5933
|
case "pen":
|
|
4415
5934
|
return this.downPen(screen, p, input);
|
|
4416
5935
|
case "pencil":
|
|
4417
|
-
return this.downPencil(p);
|
|
5936
|
+
return this.downPencil(p, input);
|
|
5937
|
+
case "width":
|
|
5938
|
+
return this.downWidth(screen, p, input);
|
|
4418
5939
|
case "scissors": {
|
|
4419
5940
|
const seg = this.nearestOutline(p);
|
|
4420
5941
|
if (seg) this.cutPathAt(seg.nodeId, seg.point);
|
|
@@ -4500,6 +6021,13 @@ var VectorEditorController = (_class3 = class {
|
|
|
4500
6021
|
this.notify();
|
|
4501
6022
|
return;
|
|
4502
6023
|
}
|
|
6024
|
+
const widget = this.hitCornerWidget(screen);
|
|
6025
|
+
if (widget) {
|
|
6026
|
+
const refs = getVectorCornerWidgets(this.cornerView()).map((w) => w.ref);
|
|
6027
|
+
this.drag = { kind: "corner", widget, refs, start: p, before: this.snapshot(), base: this.doc, moved: false, alt: !!input.alt, shift: !!input.shift };
|
|
6028
|
+
this.notify();
|
|
6029
|
+
return;
|
|
6030
|
+
}
|
|
4503
6031
|
for (const id of getEditablePathIds(this.view())) {
|
|
4504
6032
|
const node = this.getNode(id);
|
|
4505
6033
|
const seg = nearestSegment(node, this.toLocal(id, p));
|
|
@@ -4543,7 +6071,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4543
6071
|
}
|
|
4544
6072
|
downPen(screen, p, input) {
|
|
4545
6073
|
const before = this.snapshot();
|
|
4546
|
-
const sp = this.pen ? _optionalChain([this, 'access',
|
|
6074
|
+
const sp = this.pen ? _optionalChain([this, 'access', _157 => _157.getNode, 'call', _158 => _158(this.pen.nodeId), 'optionalAccess', _159 => _159.subpaths, 'access', _160 => _160[this.pen.subpath]]) : void 0;
|
|
4547
6075
|
if (this.pen && sp) {
|
|
4548
6076
|
const pen = this.pen;
|
|
4549
6077
|
const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
|
|
@@ -4557,10 +6085,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
4557
6085
|
}
|
|
4558
6086
|
const last = getAnchorWorld(this.doc, { nodeId: pen.nodeId, subpath: pen.subpath, index: sp.anchors.length - 1 });
|
|
4559
6087
|
const leave = { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null };
|
|
4560
|
-
let
|
|
4561
|
-
|
|
6088
|
+
let pt3 = this.snapDrawn(p, leave, input, () => this.drawingTargets(pen.nodeId));
|
|
6089
|
+
pt3 = this.toLocal(pen.nodeId, pt3);
|
|
4562
6090
|
const node2 = this.getNode(pen.nodeId);
|
|
4563
|
-
const anchors = [...sp.anchors, { x:
|
|
6091
|
+
const anchors = [...sp.anchors, { x: pt3.x, y: pt3.y }];
|
|
4564
6092
|
this.replaceNode(pen.nodeId, { ...node2, subpaths: node2.subpaths.map((s, i) => i === pen.subpath ? { ...s, anchors } : s) });
|
|
4565
6093
|
const ref2 = { nodeId: pen.nodeId, subpath: pen.subpath, index: anchors.length - 1 };
|
|
4566
6094
|
this.anchors = [ref2];
|
|
@@ -4588,8 +6116,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4588
6116
|
this.notify(this.doc !== before.doc);
|
|
4589
6117
|
return;
|
|
4590
6118
|
}
|
|
4591
|
-
const
|
|
4592
|
-
const node = { id: this.newId(), type: "path", name: "Path", ...this.styleFor(true), subpaths: [{ anchors: [{ x:
|
|
6119
|
+
const pt2 = this.snapDrawn(p, null, input, () => this.drawingTargets(null));
|
|
6120
|
+
const node = { id: this.newId(), type: "path", name: "Path", ...this.styleFor(true), subpaths: [{ anchors: [{ x: pt2.x, y: pt2.y }], closed: false }] };
|
|
4593
6121
|
this.setDoc([...this.doc.nodes, node]);
|
|
4594
6122
|
this.pen = { nodeId: node.id, subpath: 0 };
|
|
4595
6123
|
this.selection = [node.id];
|
|
@@ -4600,7 +6128,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4600
6128
|
this.interaction = { type: "pen", nodeId: node.id };
|
|
4601
6129
|
this.notify(true);
|
|
4602
6130
|
}
|
|
4603
|
-
downPencil(p) {
|
|
6131
|
+
downPencil(p, input = {}) {
|
|
4604
6132
|
const before = this.snapshot();
|
|
4605
6133
|
const node = {
|
|
4606
6134
|
id: this.newId(),
|
|
@@ -4614,7 +6142,11 @@ var VectorEditorController = (_class3 = class {
|
|
|
4614
6142
|
this.setDoc([...this.doc.nodes, node]);
|
|
4615
6143
|
const lazy = new LazyMouse(this.editing.pencilSmoothing * VECTOR_PENCIL_STRING_PX / this.viewport.zoom);
|
|
4616
6144
|
lazy.push([p.x, p.y]);
|
|
4617
|
-
|
|
6145
|
+
const mode = this.editing.pencilPressure;
|
|
6146
|
+
const hasPressure = typeof input.pressure === "number" && Number.isFinite(input.pressure);
|
|
6147
|
+
const record = hasPressure && (mode === "all" || mode === "pen" && input.pointerType === "pen");
|
|
6148
|
+
const pressure = hasPressure ? input.pressure : 0.5;
|
|
6149
|
+
this.drag = { kind: "pencil", nodeId: node.id, points: [[p.x, p.y]], before, lazy, pressures: record ? [pressure] : null, pressure };
|
|
4618
6150
|
this.interaction = { type: "draw", tool: "pencil", nodeId: node.id, bounds: { x: p.x, y: p.y, width: 0, height: 0 } };
|
|
4619
6151
|
this.notify(true);
|
|
4620
6152
|
}
|
|
@@ -4737,13 +6269,25 @@ var VectorEditorController = (_class3 = class {
|
|
|
4737
6269
|
case "handle": {
|
|
4738
6270
|
const anchor = getAnchorWorld(drag.base, drag.ref);
|
|
4739
6271
|
const leave = anchor ? { at: { x: anchor.x, y: anchor.y }, constrain: input.shift ? "angle" : null } : null;
|
|
4740
|
-
const
|
|
4741
|
-
const local = this.toLocal(drag.ref.nodeId,
|
|
6272
|
+
const pt2 = this.snapDrawn(p, leave, input, () => drag.targets ??= VectorSnapTargets.forHandle(drag.base, drag.ref.nodeId));
|
|
6273
|
+
const local = this.toLocal(drag.ref.nodeId, pt2, drag.base);
|
|
4742
6274
|
this.doc = this.updateAnchors(drag.base, [drag.ref], (a) => moveAnchorHandle(a, drag.ref.handle, local, input.alt));
|
|
4743
6275
|
this.interaction = { type: "handle", ref: drag.ref };
|
|
4744
6276
|
this.notify(true);
|
|
4745
6277
|
return;
|
|
4746
6278
|
}
|
|
6279
|
+
case "corner": {
|
|
6280
|
+
if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 3) return;
|
|
6281
|
+
drag.moved = true;
|
|
6282
|
+
const c = drag.widget.corner;
|
|
6283
|
+
const inv = invertMatrix(this.worldOf(drag.widget.ref.nodeId, drag.base));
|
|
6284
|
+
const d = applyMatrixLinear(inv, { x: p.x - drag.start.x, y: p.y - drag.start.y });
|
|
6285
|
+
const radius = Math.max(0, Math.min(c.maxRadius, drag.widget.drawnRadius + cornerRadiusChange(c, d)));
|
|
6286
|
+
this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, radius));
|
|
6287
|
+
this.interaction = { type: "corner", ref: drag.widget.ref, radius };
|
|
6288
|
+
this.notify(true);
|
|
6289
|
+
return;
|
|
6290
|
+
}
|
|
4747
6291
|
case "bend": {
|
|
4748
6292
|
if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 2) return;
|
|
4749
6293
|
drag.moved = true;
|
|
@@ -4769,10 +6313,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
4769
6313
|
return;
|
|
4770
6314
|
}
|
|
4771
6315
|
this.guides = null;
|
|
4772
|
-
let
|
|
4773
|
-
if (input.shift)
|
|
6316
|
+
let pt2 = p;
|
|
6317
|
+
if (input.shift) pt2 = constrain45(anchor, pt2);
|
|
4774
6318
|
const side = drag.closing ? "in" : "out";
|
|
4775
|
-
const pos = this.toLocal(drag.ref.nodeId, drag.closing ? { x: 2 * anchor.x -
|
|
6319
|
+
const pos = this.toLocal(drag.ref.nodeId, drag.closing ? { x: 2 * anchor.x - pt2.x, y: 2 * anchor.y - pt2.y } : pt2, drag.base);
|
|
4776
6320
|
this.doc = this.updateAnchors(drag.base, [drag.ref], (a) => {
|
|
4777
6321
|
if (drag.outOnly || drag.altBreaks && input.alt) return moveAnchorHandle(a, side, pos, true);
|
|
4778
6322
|
return moveAnchorHandle({ ...a, type: "symmetric" }, side, pos);
|
|
@@ -4782,7 +6326,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4782
6326
|
}
|
|
4783
6327
|
case "shape": {
|
|
4784
6328
|
const node = this.getNode(drag.nodeId);
|
|
4785
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
6329
|
+
if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) !== "path") return;
|
|
4786
6330
|
const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
|
|
4787
6331
|
let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
|
|
4788
6332
|
if (!this.snappedToObject && drag.tool !== "line" && !input.shift && !input.mod && this.snap.objects && drag.targets) end = this.matchShapeSize(drag.targets, drag.start, end, input.alt);
|
|
@@ -4811,19 +6355,43 @@ var VectorEditorController = (_class3 = class {
|
|
|
4811
6355
|
this.notify(true);
|
|
4812
6356
|
return;
|
|
4813
6357
|
}
|
|
6358
|
+
case "width": {
|
|
6359
|
+
if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 2) return;
|
|
6360
|
+
drag.moved = true;
|
|
6361
|
+
const node = findVectorNode(drag.base.nodes, drag.ref.nodeId);
|
|
6362
|
+
const local = this.toLocal(node.id, p, drag.base);
|
|
6363
|
+
let profile;
|
|
6364
|
+
if (drag.mode === "move") {
|
|
6365
|
+
const g = vectorStrokeWidthGeometry(node);
|
|
6366
|
+
const near2 = g ? nearestStrokePosition(g.line, [local.x, local.y], drag.ref.subpath) : null;
|
|
6367
|
+
if (!near2) return;
|
|
6368
|
+
profile = setWidthPoint(drag.profile, drag.ref.index, { t: near2.t });
|
|
6369
|
+
} else {
|
|
6370
|
+
const off = ((local.x - drag.origin.x) * drag.normal.x + (local.y - drag.origin.y) * drag.normal.y) / drag.unit;
|
|
6371
|
+
const patch = drag.side === "both" ? { left: Math.abs(off), right: Math.abs(off) } : drag.side === "left" ? { left: Math.max(0, off) } : { right: Math.max(0, -off) };
|
|
6372
|
+
profile = setWidthPoint(drag.profile, drag.ref.index, patch);
|
|
6373
|
+
}
|
|
6374
|
+
this.doc = { ...drag.base, nodes: mapTree(drag.base.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
|
|
6375
|
+
const w = profile[drag.ref.index];
|
|
6376
|
+
this.interaction = { type: "width", ref: drag.ref, mode: drag.mode, width: (w.left + w.right) * drag.unit };
|
|
6377
|
+
this.notify(true);
|
|
6378
|
+
return;
|
|
6379
|
+
}
|
|
4814
6380
|
case "pencil": {
|
|
4815
6381
|
const last = drag.points[drag.points.length - 1];
|
|
4816
6382
|
let moved = false;
|
|
6383
|
+
if (typeof input.pressure === "number" && Number.isFinite(input.pressure)) drag.pressure = input.pressure;
|
|
4817
6384
|
for (const q of drag.lazy.push([p.x, p.y])) {
|
|
4818
6385
|
if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
|
|
4819
6386
|
drag.points.push(q);
|
|
6387
|
+
_optionalChain([drag, 'access', _162 => _162.pressures, 'optionalAccess', _163 => _163.push, 'call', _164 => _164(drag.pressure)]);
|
|
4820
6388
|
moved = true;
|
|
4821
6389
|
}
|
|
4822
6390
|
const string = drag.lazy.string();
|
|
4823
6391
|
const node = this.getNode(drag.nodeId);
|
|
4824
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
6392
|
+
if (_optionalChain([node, 'optionalAccess', _165 => _165.type]) !== "path") return;
|
|
4825
6393
|
if (!moved) {
|
|
4826
|
-
if (string && _optionalChain([this, 'access',
|
|
6394
|
+
if (string && _optionalChain([this, 'access', _166 => _166.interaction, 'optionalAccess', _167 => _167.type]) === "draw") {
|
|
4827
6395
|
this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
|
|
4828
6396
|
this.notify();
|
|
4829
6397
|
}
|
|
@@ -4859,8 +6427,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4859
6427
|
}
|
|
4860
6428
|
this.setGuides(res);
|
|
4861
6429
|
return {
|
|
4862
|
-
dx: _optionalChain([res, 'optionalAccess',
|
|
4863
|
-
dy: _optionalChain([res, 'optionalAccess',
|
|
6430
|
+
dx: _optionalChain([res, 'optionalAccess', _168 => _168.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
|
|
6431
|
+
dy: _optionalChain([res, 'optionalAccess', _169 => _169.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
|
|
4864
6432
|
};
|
|
4865
6433
|
}
|
|
4866
6434
|
/** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
|
|
@@ -4885,19 +6453,19 @@ var VectorEditorController = (_class3 = class {
|
|
|
4885
6453
|
const guides = { lines: [], labels: [] };
|
|
4886
6454
|
for (const axis of ["x", "y"]) {
|
|
4887
6455
|
const d = end[axis] - start[axis];
|
|
4888
|
-
const
|
|
4889
|
-
if (
|
|
4890
|
-
out[axis] = start[axis] + Math.sign(d) *
|
|
6456
|
+
const len2 = Math.abs(d) > 1e-9 ? targets.matchSize(Math.abs(d) * k, tol) : null;
|
|
6457
|
+
if (len2 === null || len2 <= 0) continue;
|
|
6458
|
+
out[axis] = start[axis] + Math.sign(d) * len2 / k;
|
|
4891
6459
|
const from = { ...out, [axis]: fromCentre ? 2 * start[axis] - out[axis] : start[axis] };
|
|
4892
6460
|
guides.lines.push({ kind: "size", x1: from.x, y1: from.y, x2: out.x, y2: out.y });
|
|
4893
|
-
guides.labels.push({ kind: "size", x: (from.x + out.x) / 2, y: (from.y + out.y) / 2, text: `${axis === "x" ? "W" : "H"} ${Math.round(
|
|
6461
|
+
guides.labels.push({ kind: "size", x: (from.x + out.x) / 2, y: (from.y + out.y) / 2, text: `${axis === "x" ? "W" : "H"} ${Math.round(len2 * 100) / 100}` });
|
|
4894
6462
|
}
|
|
4895
6463
|
this.setGuides(guides);
|
|
4896
6464
|
return out;
|
|
4897
6465
|
}
|
|
4898
6466
|
/** Primary button released. */
|
|
4899
6467
|
pointerUp(screen, input = {}) {
|
|
4900
|
-
if (screen && this.drag && this.drag.kind !== "pan") this.pointerMove(screen, input);
|
|
6468
|
+
if (screen && this.drag && this.drag.kind !== "pan") this.pointerMove(screen, { ...input, pressure: void 0 });
|
|
4901
6469
|
const drag = this.drag;
|
|
4902
6470
|
this.drag = null;
|
|
4903
6471
|
this.interaction = null;
|
|
@@ -4935,6 +6503,29 @@ var VectorEditorController = (_class3 = class {
|
|
|
4935
6503
|
return;
|
|
4936
6504
|
}
|
|
4937
6505
|
return this.commit("Bend Segment", drag.before);
|
|
6506
|
+
case "corner": {
|
|
6507
|
+
if (drag.moved) return this.commit("Corner Radius", drag.before);
|
|
6508
|
+
if (drag.alt) {
|
|
6509
|
+
const kind = nextVectorCornerKind(drag.widget.kind);
|
|
6510
|
+
this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _170 => _170.corner, 'optionalAccess', _171 => _171.radius]), () => ( 0)), kind));
|
|
6511
|
+
return this.commit("Corner Kind", drag.before);
|
|
6512
|
+
}
|
|
6513
|
+
const ref = drag.widget.ref;
|
|
6514
|
+
const has = this.anchors.some((a) => sameRef(a, ref));
|
|
6515
|
+
this.editingId = ref.nodeId;
|
|
6516
|
+
this.anchors = drag.shift ? has ? this.anchors.filter((a) => !sameRef(a, ref)) : [...this.anchors, ref] : [ref];
|
|
6517
|
+
this.notify();
|
|
6518
|
+
return;
|
|
6519
|
+
}
|
|
6520
|
+
case "width": {
|
|
6521
|
+
if (drag.moved) return this.commit(drag.mode === "move" ? "Move Width Point" : "Width Point", drag.before);
|
|
6522
|
+
if (drag.created) {
|
|
6523
|
+
this.doc = drag.before.doc;
|
|
6524
|
+
this.widthPoint = null;
|
|
6525
|
+
this.notify(true);
|
|
6526
|
+
} else this.notify();
|
|
6527
|
+
return;
|
|
6528
|
+
}
|
|
4938
6529
|
case "stroke": {
|
|
4939
6530
|
this.notify();
|
|
4940
6531
|
if (drag.tool === "knife") this.knife(drag.points);
|
|
@@ -4960,19 +6551,22 @@ var VectorEditorController = (_class3 = class {
|
|
|
4960
6551
|
}
|
|
4961
6552
|
case "pencil": {
|
|
4962
6553
|
const node = this.getNode(drag.nodeId);
|
|
4963
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
6554
|
+
if (_optionalChain([node, 'optionalAccess', _172 => _172.type]) !== "path" || drag.points.length < 2) {
|
|
4964
6555
|
this.doc = drag.before.doc;
|
|
4965
6556
|
this.notify(true);
|
|
4966
6557
|
return;
|
|
4967
6558
|
}
|
|
4968
|
-
for (const q of drag.lazy.finish())
|
|
6559
|
+
for (const q of drag.lazy.finish()) {
|
|
6560
|
+
drag.points.push(q);
|
|
6561
|
+
_optionalChain([drag, 'access', _173 => _173.pressures, 'optionalAccess', _174 => _174.push, 'call', _175 => _175(drag.pressure)]);
|
|
6562
|
+
}
|
|
4969
6563
|
const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
|
|
4970
6564
|
if (shape) {
|
|
4971
6565
|
this.replaceNode(drag.nodeId, { ...node, name: inkShapeLabel(shape), subpaths: [inkShapeSubpath(shape)] });
|
|
4972
6566
|
this.selection = [drag.nodeId];
|
|
4973
6567
|
return this.commit(`Draw ${inkShapeLabel(shape)}`, drag.before);
|
|
4974
6568
|
}
|
|
4975
|
-
const fitted =
|
|
6569
|
+
const fitted = _chunkGVB7ZPLZcjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
|
|
4976
6570
|
const anchors = [];
|
|
4977
6571
|
for (const c of fitted) {
|
|
4978
6572
|
if (!anchors.length) anchors.push({ x: c.p0[0], y: c.p0[1] });
|
|
@@ -4980,7 +6574,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4980
6574
|
anchors.push({ x: c.p3[0], y: c.p3[1], in: { x: c.p2[0], y: c.p2[1] } });
|
|
4981
6575
|
}
|
|
4982
6576
|
const subpaths = anchors.length > 1 ? bezierToSubpaths(subpathsToBezier([{ anchors, closed: false }])) : node.subpaths;
|
|
4983
|
-
|
|
6577
|
+
const profile = drag.pressures ? pencilPressureProfile(drag.points, drag.pressures) : [];
|
|
6578
|
+
this.replaceNode(drag.nodeId, { ...node, subpaths, ...profile.length ? { strokeWidthProfile: profile } : {} });
|
|
4984
6579
|
this.selection = [drag.nodeId];
|
|
4985
6580
|
return this.commit("Draw", drag.before);
|
|
4986
6581
|
}
|
|
@@ -5034,7 +6629,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5034
6629
|
this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
|
|
5035
6630
|
return;
|
|
5036
6631
|
}
|
|
5037
|
-
const id = _nullishCoalesce(_optionalChain([this, 'access',
|
|
6632
|
+
const id = _nullishCoalesce(_optionalChain([this, 'access', _176 => _176.hover, 'optionalAccess', _177 => _177.segment, 'optionalAccess', _178 => _178.nodeId]), () => ( this.editingId));
|
|
5038
6633
|
if (id) {
|
|
5039
6634
|
const node = this.getNode(id);
|
|
5040
6635
|
const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
|
|
@@ -5102,7 +6697,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
5102
6697
|
switch (key) {
|
|
5103
6698
|
case "Escape":
|
|
5104
6699
|
if (this.drag) this.cancelDrag();
|
|
5105
|
-
else if (this.
|
|
6700
|
+
else if (this.tool === "width" && this.widthPoint) {
|
|
6701
|
+
this.widthPoint = null;
|
|
6702
|
+
this.notify();
|
|
6703
|
+
} else if (this.pen) this.finishPath();
|
|
5106
6704
|
else if (this.tool === "direct" && this.anchors.length) {
|
|
5107
6705
|
this.anchors = [];
|
|
5108
6706
|
this.notify();
|
|
@@ -5114,7 +6712,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5114
6712
|
this.finishPath();
|
|
5115
6713
|
return true;
|
|
5116
6714
|
}
|
|
5117
|
-
if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access',
|
|
6715
|
+
if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _179 => _179.getNode, 'call', _180 => _180(this.selection[0]), 'optionalAccess', _181 => _181.type]) === "path") {
|
|
5118
6716
|
this.tool = "direct";
|
|
5119
6717
|
this.editingId = this.selection[0];
|
|
5120
6718
|
this.notify();
|
|
@@ -5123,6 +6721,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5123
6721
|
return false;
|
|
5124
6722
|
case "Delete":
|
|
5125
6723
|
case "Backspace":
|
|
6724
|
+
if (this.tool === "width" && this.widthPoint) return this.removeWidthPoint(this.widthPoint).ok;
|
|
5126
6725
|
if (this.pen) {
|
|
5127
6726
|
const node = this.getNode(this.pen.nodeId);
|
|
5128
6727
|
const sp = node.subpaths[this.pen.subpath];
|
|
@@ -5179,9 +6778,9 @@ function inkShapeSubpath(shape) {
|
|
|
5179
6778
|
function constrain45(origin, p) {
|
|
5180
6779
|
const dx = p.x - origin.x;
|
|
5181
6780
|
const dy = p.y - origin.y;
|
|
5182
|
-
const
|
|
6781
|
+
const len2 = Math.hypot(dx, dy);
|
|
5183
6782
|
const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
|
|
5184
|
-
return { x: origin.x + Math.cos(a) *
|
|
6783
|
+
return { x: origin.x + Math.cos(a) * len2, y: origin.y + Math.sin(a) * len2 };
|
|
5185
6784
|
}
|
|
5186
6785
|
function scaleFor(handle, b, p, mods = {}) {
|
|
5187
6786
|
const west = handle.includes("w");
|
|
@@ -5204,6 +6803,31 @@ function scaleFor(handle, b, p, mods = {}) {
|
|
|
5204
6803
|
}
|
|
5205
6804
|
return { sx, sy, origin };
|
|
5206
6805
|
}
|
|
6806
|
+
function withStyle(node, style) {
|
|
6807
|
+
const next = { ...node, ...style };
|
|
6808
|
+
if ("strokeWidthProfile" in style) {
|
|
6809
|
+
const p = style.strokeWidthProfile ? sanitizeWidthProfile(style.strokeWidthProfile) : [];
|
|
6810
|
+
if (p.length) next.strokeWidthProfile = p;
|
|
6811
|
+
else delete next.strokeWidthProfile;
|
|
6812
|
+
}
|
|
6813
|
+
return next;
|
|
6814
|
+
}
|
|
6815
|
+
function hiddenOrLocked(nodes, id) {
|
|
6816
|
+
const loc = locateVectorNode(nodes, id);
|
|
6817
|
+
if (!loc) return false;
|
|
6818
|
+
const n = loc.list[loc.index];
|
|
6819
|
+
if (n.locked || n.visible === false) return true;
|
|
6820
|
+
return loc.parent ? hiddenOrLocked(nodes, loc.parent.id) : false;
|
|
6821
|
+
}
|
|
6822
|
+
function pencilPressureProfile(points, pressures) {
|
|
6823
|
+
const n = Math.min(points.length, pressures.length);
|
|
6824
|
+
if (n < 2) return [];
|
|
6825
|
+
const s = [0];
|
|
6826
|
+
for (let i = 1; i < n; i++) s.push(s[i - 1] + Math.hypot(points[i][0] - points[i - 1][0], points[i][1] - points[i - 1][1]));
|
|
6827
|
+
const total = s[n - 1];
|
|
6828
|
+
if (!(total > 0)) return [];
|
|
6829
|
+
return pressureWidthProfile(s.map((d, i) => ({ t: d / total, pressure: pressures[i] })));
|
|
6830
|
+
}
|
|
5207
6831
|
|
|
5208
6832
|
// src/vector/render.ts
|
|
5209
6833
|
var fmt = (n) => Math.round(n * 100) / 100;
|
|
@@ -5218,17 +6842,19 @@ function renderNode(node, defs, idPrefix) {
|
|
|
5218
6842
|
const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`);
|
|
5219
6843
|
if (fill.gradient) defs.push(fill.gradient);
|
|
5220
6844
|
if (stroke.gradient) defs.push(stroke.gradient);
|
|
6845
|
+
const strokeParts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${idPrefix}clip-${node.id}`) : void 0;
|
|
5221
6846
|
return {
|
|
5222
6847
|
kind: "path",
|
|
5223
6848
|
id: node.id,
|
|
5224
6849
|
...node.transform ? { transform: fmtMatrix(node.transform) } : {},
|
|
6850
|
+
...strokeParts ? { strokeParts } : {},
|
|
5225
6851
|
d: vectorPathData(node),
|
|
5226
6852
|
fill: fill.value,
|
|
5227
6853
|
stroke: stroke.value,
|
|
5228
6854
|
strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
|
|
5229
6855
|
...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
|
|
5230
6856
|
...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
|
|
5231
|
-
..._optionalChain([node, 'access',
|
|
6857
|
+
..._optionalChain([node, 'access', _182 => _182.strokeDasharray, 'optionalAccess', _183 => _183.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
|
|
5232
6858
|
fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
|
|
5233
6859
|
opacity: _nullishCoalesce(node.opacity, () => ( 1))
|
|
5234
6860
|
};
|
|
@@ -5237,9 +6863,9 @@ function getVectorSceneRenderData(state, options = {}) {
|
|
|
5237
6863
|
const defs = [];
|
|
5238
6864
|
const prefix = _nullishCoalesce(options.idPrefix, () => ( "ve-"));
|
|
5239
6865
|
const nodes = state.document.nodes.map((n) => renderNode(n, defs, prefix)).filter((n) => !!n);
|
|
5240
|
-
const
|
|
6866
|
+
const v2 = state.viewport;
|
|
5241
6867
|
return {
|
|
5242
|
-
transform: `translate(${fmt(
|
|
6868
|
+
transform: `translate(${fmt(v2.x)} ${fmt(v2.y)}) scale(${v2.zoom})`,
|
|
5243
6869
|
width: state.document.width,
|
|
5244
6870
|
height: state.document.height,
|
|
5245
6871
|
...state.document.background ? { background: state.document.background } : {},
|
|
@@ -5249,10 +6875,10 @@ function getVectorSceneRenderData(state, options = {}) {
|
|
|
5249
6875
|
}
|
|
5250
6876
|
var toScreen = (state, p) => ({ x: p.x * state.viewport.zoom + state.viewport.x, y: p.y * state.viewport.zoom + state.viewport.y });
|
|
5251
6877
|
function screenOutline(state, node) {
|
|
5252
|
-
const
|
|
5253
|
-
const view = [
|
|
6878
|
+
const v2 = state.viewport;
|
|
6879
|
+
const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
|
|
5254
6880
|
const parent = _nullishCoalesce(vectorParentMatrix(state.document.nodes, node.id), () => ( IDENTITY_MATRIX));
|
|
5255
|
-
return collectWithMatrix(node, parent).map(({ path, parent: m }) =>
|
|
6881
|
+
return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
|
|
5256
6882
|
}
|
|
5257
6883
|
function collectWithMatrix(node, parent) {
|
|
5258
6884
|
if (node.type === "path") return [{ path: node, parent }];
|
|
@@ -5277,9 +6903,9 @@ function getVectorOverlayRenderData(state) {
|
|
|
5277
6903
|
};
|
|
5278
6904
|
for (const id of state.selection) outline(id, true);
|
|
5279
6905
|
if (state.editingId) outline(state.editingId, true);
|
|
5280
|
-
if (_optionalChain([state, 'access',
|
|
6906
|
+
if (_optionalChain([state, 'access', _184 => _184.hover, 'optionalAccess', _185 => _185.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
|
|
5281
6907
|
let selectionBox = null;
|
|
5282
|
-
if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access',
|
|
6908
|
+
if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _186 => _186.interaction, 'optionalAccess', _187 => _187.type]) !== "marquee") {
|
|
5283
6909
|
const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
|
|
5284
6910
|
selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
|
|
5285
6911
|
}
|
|
@@ -5289,9 +6915,9 @@ function getVectorOverlayRenderData(state) {
|
|
|
5289
6915
|
const a = getAnchorWorld(doc, ref);
|
|
5290
6916
|
const s = toScreen(state, a);
|
|
5291
6917
|
const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
|
|
5292
|
-
return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access',
|
|
6918
|
+
return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _188 => _188.hover, 'optionalAccess', _189 => _189.anchor]), ref), closes };
|
|
5293
6919
|
});
|
|
5294
|
-
if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access',
|
|
6920
|
+
if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _190 => _190.hover, 'optionalAccess', _191 => _191.anchor])) {
|
|
5295
6921
|
const a = getAnchorWorld(doc, state.hover.anchor);
|
|
5296
6922
|
if (a) {
|
|
5297
6923
|
const s = toScreen(state, a);
|
|
@@ -5300,15 +6926,15 @@ function getVectorOverlayRenderData(state) {
|
|
|
5300
6926
|
}
|
|
5301
6927
|
const handles = getVisibleHandles(state).flatMap((ref) => {
|
|
5302
6928
|
const a = getAnchorWorld(doc, ref);
|
|
5303
|
-
const h = _optionalChain([a, 'optionalAccess',
|
|
6929
|
+
const h = _optionalChain([a, 'optionalAccess', _192 => _192[ref.handle]]);
|
|
5304
6930
|
if (!a || !h) return [];
|
|
5305
6931
|
const s = toScreen(state, h);
|
|
5306
6932
|
const sa = toScreen(state, a);
|
|
5307
|
-
const hovered = !!_optionalChain([state, 'access',
|
|
6933
|
+
const hovered = !!_optionalChain([state, 'access', _193 => _193.hover, 'optionalAccess', _194 => _194.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
|
|
5308
6934
|
return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
|
|
5309
6935
|
});
|
|
5310
6936
|
let marquee = null;
|
|
5311
|
-
if (_optionalChain([state, 'access',
|
|
6937
|
+
if (_optionalChain([state, 'access', _195 => _195.interaction, 'optionalAccess', _196 => _196.type]) === "marquee") {
|
|
5312
6938
|
const b = state.interaction.bounds;
|
|
5313
6939
|
const tl = toScreen(state, b);
|
|
5314
6940
|
marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
|
|
@@ -5316,7 +6942,7 @@ function getVectorOverlayRenderData(state) {
|
|
|
5316
6942
|
let penPreview = null;
|
|
5317
6943
|
if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
|
|
5318
6944
|
const node = findVectorNode(doc.nodes, state.pen.nodeId);
|
|
5319
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
6945
|
+
const sp = _optionalChain([node, 'optionalAccess', _197 => _197.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
|
|
5320
6946
|
const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
|
|
5321
6947
|
if (last) {
|
|
5322
6948
|
const a = toScreen(state, last);
|
|
@@ -5329,9 +6955,9 @@ function getVectorOverlayRenderData(state) {
|
|
|
5329
6955
|
const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
|
|
5330
6956
|
let status = null;
|
|
5331
6957
|
const it = state.interaction;
|
|
5332
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
5333
|
-
else if (_optionalChain([it, 'optionalAccess',
|
|
5334
|
-
else if (_optionalChain([it, 'optionalAccess',
|
|
6958
|
+
if (_optionalChain([it, 'optionalAccess', _198 => _198.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
|
|
6959
|
+
else if (_optionalChain([it, 'optionalAccess', _199 => _199.type]) === "move" || _optionalChain([it, 'optionalAccess', _200 => _200.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
|
|
6960
|
+
else if (_optionalChain([it, 'optionalAccess', _201 => _201.type]) === "scale" || _optionalChain([it, 'optionalAccess', _202 => _202.type]) === "draw") {
|
|
5335
6961
|
const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
|
|
5336
6962
|
if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
|
|
5337
6963
|
}
|
|
@@ -5348,19 +6974,39 @@ function getVectorOverlayRenderData(state) {
|
|
|
5348
6974
|
}
|
|
5349
6975
|
}
|
|
5350
6976
|
let toolStroke = null;
|
|
5351
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
6977
|
+
if (_optionalChain([it, 'optionalAccess', _203 => _203.type]) === "stroke") {
|
|
5352
6978
|
const pts = it.points.map((q) => toScreen(state, q));
|
|
5353
6979
|
const d = pts.map((q, i) => `${i ? "L" : "M"}${fmt(q.x)} ${fmt(q.y)}`).join("") + (pts.length === 1 ? `L${fmt(pts[0].x)} ${fmt(pts[0].y)}` : "");
|
|
5354
6980
|
toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
|
|
5355
6981
|
}
|
|
5356
6982
|
let pencilString = null;
|
|
5357
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
6983
|
+
if (_optionalChain([it, 'optionalAccess', _204 => _204.type]) === "draw" && it.string) {
|
|
5358
6984
|
const a = toScreen(state, it.string[0]);
|
|
5359
6985
|
const b = toScreen(state, it.string[1]);
|
|
5360
6986
|
pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
|
|
5361
6987
|
}
|
|
5362
|
-
const
|
|
5363
|
-
|
|
6988
|
+
const dragged = _optionalChain([it, 'optionalAccess', _205 => _205.type]) === "corner" ? it.ref : null;
|
|
6989
|
+
const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
|
|
6990
|
+
const s = toScreen(state, w);
|
|
6991
|
+
return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _206 => _206.hover, 'optionalAccess', _207 => _207.corner]), w.ref), active: !!dragged };
|
|
6992
|
+
});
|
|
6993
|
+
if (_optionalChain([it, 'optionalAccess', _208 => _208.type]) === "corner") status = `R ${fmt(it.radius)}`;
|
|
6994
|
+
const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _209 => _209.hover, 'optionalAccess', _210 => _210.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
|
|
6995
|
+
const sc = (p) => {
|
|
6996
|
+
const q = toScreen(state, p);
|
|
6997
|
+
return { x: fmt(q.x), y: fmt(q.y) };
|
|
6998
|
+
};
|
|
6999
|
+
const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
|
|
7000
|
+
const widthPoints = getVectorWidthHandles(state).map((h) => {
|
|
7001
|
+
const c = sc(h);
|
|
7002
|
+
const hw = _optionalChain([state, 'access', _211 => _211.hover, 'optionalAccess', _212 => _212.widthPoint]);
|
|
7003
|
+
const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
|
|
7004
|
+
return { ref: h.ref, x: c.x, y: c.y, left: sc(h.left), right: sc(h.right), selected: h.selected, hovered, active: _optionalChain([it, 'optionalAccess', _213 => _213.type]) === "width" && sameWidth(it.ref, h.ref) };
|
|
7005
|
+
});
|
|
7006
|
+
const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _214 => _214.hover, 'optionalAccess', _215 => _215.strokeSpot]) : null;
|
|
7007
|
+
const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
|
|
7008
|
+
if (_optionalChain([it, 'optionalAccess', _216 => _216.type]) === "width") status = it.mode === "move" ? "Move width point" : `W ${fmt(it.width)}`;
|
|
7009
|
+
return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot };
|
|
5364
7010
|
}
|
|
5365
7011
|
var VECTOR_OVERLAY_THEME = {
|
|
5366
7012
|
accent: "#0ea5e9",
|
|
@@ -5372,13 +7018,25 @@ var VECTOR_OVERLAY_THEME = {
|
|
|
5372
7018
|
eraserFill: "rgba(239, 68, 68, 0.25)",
|
|
5373
7019
|
transformHandleSize: 8,
|
|
5374
7020
|
anchorSize: 7,
|
|
5375
|
-
handleRadius: 3.5
|
|
7021
|
+
handleRadius: 3.5,
|
|
7022
|
+
/** Radius of a live corner widget (a ring with a dot). */
|
|
7023
|
+
cornerWidgetRadius: 4,
|
|
7024
|
+
/** Half the diagonal of a width point's diamond. */
|
|
7025
|
+
widthPointSize: 5
|
|
5376
7026
|
};
|
|
7027
|
+
function vectorDiamondPath(x, y, r = VECTOR_OVERLAY_THEME.widthPointSize) {
|
|
7028
|
+
return `M${fmt(x)} ${fmt(y - r)}L${fmt(x + r)} ${fmt(y)}L${fmt(x)} ${fmt(y + r)}L${fmt(x - r)} ${fmt(y)}Z`;
|
|
7029
|
+
}
|
|
5377
7030
|
function toVectorModifiers(event) {
|
|
5378
7031
|
return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
|
|
5379
7032
|
}
|
|
5380
7033
|
function toVectorPointerInput(event) {
|
|
5381
|
-
return {
|
|
7034
|
+
return {
|
|
7035
|
+
...toVectorModifiers(event),
|
|
7036
|
+
button: event.button,
|
|
7037
|
+
...typeof event.pressure === "number" ? { pressure: event.pressure } : {},
|
|
7038
|
+
...event.pointerType ? { pointerType: event.pointerType } : {}
|
|
7039
|
+
};
|
|
5382
7040
|
}
|
|
5383
7041
|
function toVectorKeyInput(event) {
|
|
5384
7042
|
return { key: event.key, ...toVectorModifiers(event) };
|
|
@@ -5388,7 +7046,7 @@ function isVectorTextTarget(target) {
|
|
|
5388
7046
|
if (!el || typeof el !== "object") return false;
|
|
5389
7047
|
const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
|
|
5390
7048
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
5391
|
-
return !!el.isContentEditable || _optionalChain([el, 'access',
|
|
7049
|
+
return !!el.isContentEditable || _optionalChain([el, 'access', _217 => _217.getAttribute, 'optionalCall', _218 => _218("contenteditable")]) === "true";
|
|
5392
7050
|
}
|
|
5393
7051
|
function getVectorSelectionStyle(state) {
|
|
5394
7052
|
const paths = [];
|
|
@@ -5396,18 +7054,75 @@ function getVectorSelectionStyle(state) {
|
|
|
5396
7054
|
const node = findVectorNode(state.document.nodes, id);
|
|
5397
7055
|
if (node) paths.push(...collect(node));
|
|
5398
7056
|
}
|
|
7057
|
+
const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _219 => _219.selectedAnchors, 'optionalAccess', _220 => _220.length]);
|
|
7058
|
+
if (anchorMode) {
|
|
7059
|
+
for (const r of state.selectedAnchors) {
|
|
7060
|
+
if (paths.some((p) => p.id === r.nodeId)) continue;
|
|
7061
|
+
const node = findVectorNode(state.document.nodes, r.nodeId);
|
|
7062
|
+
if (_optionalChain([node, 'optionalAccess', _221 => _221.type]) === "path") paths.push(node);
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
5399
7065
|
if (!paths.length) return null;
|
|
5400
7066
|
const first = paths[0];
|
|
5401
7067
|
const same = (get) => paths.every((p) => JSON.stringify(_nullishCoalesce(get(p), () => ( null))) === JSON.stringify(_nullishCoalesce(get(first), () => ( null))));
|
|
7068
|
+
const corners2 = [];
|
|
7069
|
+
for (const p of paths) {
|
|
7070
|
+
for (const c of vectorCorners(p.subpaths)) {
|
|
7071
|
+
if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
|
|
7072
|
+
const corner = p.subpaths[c.subpath].anchors[c.index].corner;
|
|
7073
|
+
corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _222 => _222.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _223 => _223.kind]), () => ( "round")) });
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
|
|
5402
7077
|
return {
|
|
5403
7078
|
fill: _nullishCoalesce(first.fill, () => ( null)),
|
|
5404
7079
|
stroke: _nullishCoalesce(first.stroke, () => ( null)),
|
|
5405
7080
|
strokeWidth: _nullishCoalesce(first.strokeWidth, () => ( 1)),
|
|
5406
7081
|
opacity: _nullishCoalesce(first.opacity, () => ( 1)),
|
|
5407
|
-
|
|
7082
|
+
strokeAlign: _nullishCoalesce(first.strokeAlign, () => ( "center")),
|
|
7083
|
+
startArrow: _nullishCoalesce(first.startArrow, () => ( null)),
|
|
7084
|
+
endArrow: _nullishCoalesce(first.endArrow, () => ( null)),
|
|
7085
|
+
arrowAlign: _nullishCoalesce(first.arrowAlign, () => ( "extend")),
|
|
7086
|
+
startArrowScale: _nullishCoalesce(first.startArrowScale, () => ( 1)),
|
|
7087
|
+
endArrowScale: _nullishCoalesce(first.endArrowScale, () => ( 1)),
|
|
7088
|
+
strokeWidthProfile: profileOf(first),
|
|
7089
|
+
widthProfile: widthProfilePresetId(profileOf(first)),
|
|
7090
|
+
cornerRadius: c0.radius,
|
|
7091
|
+
cornerKind: c0.kind,
|
|
7092
|
+
corners: corners2.length,
|
|
7093
|
+
hasOpenPath: paths.some((p) => p.subpaths.some((sp) => !sp.closed && sp.anchors.length > 1)),
|
|
7094
|
+
mixed: {
|
|
7095
|
+
fill: !same((p) => p.fill),
|
|
7096
|
+
stroke: !same((p) => p.stroke),
|
|
7097
|
+
strokeWidth: !same((p) => _nullishCoalesce(p.strokeWidth, () => ( 1))),
|
|
7098
|
+
opacity: !same((p) => _nullishCoalesce(p.opacity, () => ( 1))),
|
|
7099
|
+
strokeAlign: !same((p) => _nullishCoalesce(p.strokeAlign, () => ( "center"))),
|
|
7100
|
+
startArrow: !same((p) => _nullishCoalesce(p.startArrow, () => ( null))),
|
|
7101
|
+
endArrow: !same((p) => _nullishCoalesce(p.endArrow, () => ( null))),
|
|
7102
|
+
arrowAlign: !same((p) => _nullishCoalesce(p.arrowAlign, () => ( "extend"))),
|
|
7103
|
+
startArrowScale: !same((p) => _nullishCoalesce(p.startArrowScale, () => ( 1))),
|
|
7104
|
+
endArrowScale: !same((p) => _nullishCoalesce(p.endArrowScale, () => ( 1))),
|
|
7105
|
+
widthProfile: !same(profileOf),
|
|
7106
|
+
cornerRadius: corners2.some((c) => c.radius !== c0.radius),
|
|
7107
|
+
cornerKind: corners2.some((c) => c.kind !== c0.kind)
|
|
7108
|
+
},
|
|
5408
7109
|
pathIds: paths.map((p) => p.id)
|
|
5409
7110
|
};
|
|
5410
7111
|
}
|
|
7112
|
+
function profileOf(p) {
|
|
7113
|
+
const points = sanitizeWidthProfile(p.strokeWidthProfile);
|
|
7114
|
+
return points.length && widthProfilePresetId(points) !== "uniform" ? points : null;
|
|
7115
|
+
}
|
|
7116
|
+
var VECTOR_STROKE_ALIGN_LABELS = { center: "Center", inside: "Inside", outside: "Outside" };
|
|
7117
|
+
var VECTOR_CORNER_KIND_LABELS = { round: "Round", inverted: "Inverted", chamfer: "Chamfer" };
|
|
7118
|
+
var VECTOR_ARROWHEAD_LABELS = {
|
|
7119
|
+
triangle: "Triangle",
|
|
7120
|
+
arrow: "Arrow",
|
|
7121
|
+
open: "Open arrow",
|
|
7122
|
+
circle: "Circle",
|
|
7123
|
+
square: "Square",
|
|
7124
|
+
bar: "Bar"
|
|
7125
|
+
};
|
|
5411
7126
|
function vectorPaintToHex(paint) {
|
|
5412
7127
|
if (typeof paint !== "string") return null;
|
|
5413
7128
|
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(paint.trim());
|
|
@@ -5420,7 +7135,8 @@ var VECTOR_PATH_ACTIONS = [
|
|
|
5420
7135
|
{ id: "average", label: "\u22B9", title: "Average Points", shortcut: "Alt+J", enabled: (_s, p) => p > 1, run: (c) => c.averageAnchors("both") },
|
|
5421
7136
|
{ id: "cut", label: "\u2702\xB7", title: "Cut at Points", enabled: (_s, p) => p > 0, run: (c) => c.cutAtAnchors() },
|
|
5422
7137
|
{ id: "clean", label: "\u2301", title: "Remove Redundant Points", enabled: (s, p) => s + p > 0, run: (c) => c.removeRedundantPoints() },
|
|
5423
|
-
{ id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() }
|
|
7138
|
+
{ id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() },
|
|
7139
|
+
{ id: "outline", label: "\u22A1", title: "Outline Stroke", enabled: (s) => s > 0, run: (c) => c.outlineStroke() }
|
|
5424
7140
|
];
|
|
5425
7141
|
var VECTOR_EDIT_TOGGLES = [
|
|
5426
7142
|
{ key: "keepShapeOnDelete", label: "Keep", title: "Keep shape when deleting points" },
|
|
@@ -5448,6 +7164,19 @@ var POINTS = {
|
|
|
5448
7164
|
minItems: 1,
|
|
5449
7165
|
description: "Polyline [[x, y], \u2026] in document units."
|
|
5450
7166
|
};
|
|
7167
|
+
var WIDTH_POINTS = {
|
|
7168
|
+
type: ["array", "null"],
|
|
7169
|
+
items: {
|
|
7170
|
+
type: "object",
|
|
7171
|
+
properties: {
|
|
7172
|
+
t: { type: "number", minimum: 0, maximum: 1, description: "Position along each subpath by length (0 start, 1 end)." },
|
|
7173
|
+
left: { type: "number", minimum: 0, description: "Left side as a multiple of half the stroke width (1 = uniform)." },
|
|
7174
|
+
right: { type: "number", minimum: 0, description: "Right side (looking along the path, y down)." }
|
|
7175
|
+
},
|
|
7176
|
+
required: ["t", "left", "right"]
|
|
7177
|
+
},
|
|
7178
|
+
description: "Variable-width profile points, interpolated smoothly (monotone cubic); two points at one `t` make a step. `null` or `[]` strokes at a uniform width."
|
|
7179
|
+
};
|
|
5451
7180
|
var PAINT = {
|
|
5452
7181
|
description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
|
|
5453
7182
|
};
|
|
@@ -5458,7 +7187,14 @@ var STYLE_PROPS = {
|
|
|
5458
7187
|
strokeLinecap: { type: "string", enum: ["butt", "round", "square"] },
|
|
5459
7188
|
strokeLinejoin: { type: "string", enum: ["miter", "round", "bevel"] },
|
|
5460
7189
|
strokeDasharray: { type: "array", items: { type: "number" } },
|
|
5461
|
-
fillRule: { type: "string", enum: ["nonzero", "evenodd"] }
|
|
7190
|
+
fillRule: { type: "string", enum: ["nonzero", "evenodd"] },
|
|
7191
|
+
strokeAlign: { type: "string", enum: ["center", "inside", "outside"], description: "Where the stroke sits on the outline (closed shapes only; open paths stroke centred)." },
|
|
7192
|
+
startArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the start of an open path, or null for none." },
|
|
7193
|
+
endArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the end of an open path, or null for none." },
|
|
7194
|
+
arrowAlign: { type: "string", enum: ["extend", "tip"], description: "`extend`: the tip sits past the end point; `tip`: on it, the stroke shortened under the head." },
|
|
7195
|
+
startArrowScale: { type: "number", exclusiveMinimum: 0, description: "Start head size as a multiple of the stroke width (default 1)." },
|
|
7196
|
+
endArrowScale: { type: "number", exclusiveMinimum: 0, description: "End head size as a multiple of the stroke width (default 1)." },
|
|
7197
|
+
strokeWidthProfile: WIDTH_POINTS
|
|
5462
7198
|
};
|
|
5463
7199
|
var pickStyle = (params) => {
|
|
5464
7200
|
const out = {};
|
|
@@ -5474,17 +7210,17 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5474
7210
|
const base = { group, ...options.scope ? { scope: options.scope } : {} };
|
|
5475
7211
|
const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
|
|
5476
7212
|
const ids = (params) => {
|
|
5477
|
-
const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess',
|
|
7213
|
+
const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _224 => _224.ids]), () => ( controller.getState().selection));
|
|
5478
7214
|
for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
|
|
5479
7215
|
if (!list2.length) fail("Nothing is selected; pass ids.");
|
|
5480
7216
|
return list2;
|
|
5481
7217
|
};
|
|
5482
|
-
const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess',
|
|
7218
|
+
const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _225 => _225.ids, 'optionalAccess', _226 => _226.length]) ? true : hasSelection();
|
|
5483
7219
|
const anchorsOf = (refs) => {
|
|
5484
7220
|
if (!refs) return [...controller.getState().selectedAnchors];
|
|
5485
7221
|
for (const r of refs) {
|
|
5486
7222
|
const node = controller.getNode(r.nodeId);
|
|
5487
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
7223
|
+
if (_optionalChain([node, 'optionalAccess', _227 => _227.type]) !== "path" || !_optionalChain([node, 'access', _228 => _228.subpaths, 'access', _229 => _229[r.subpath], 'optionalAccess', _230 => _230.anchors, 'access', _231 => _231[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
|
|
5488
7224
|
}
|
|
5489
7225
|
return refs;
|
|
5490
7226
|
};
|
|
@@ -5551,7 +7287,8 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5551
7287
|
y: { type: "number" },
|
|
5552
7288
|
width: { type: "number", description: "For a line: the x offset of its end." },
|
|
5553
7289
|
height: { type: "number", description: "For a line: the y offset of its end." },
|
|
5554
|
-
cornerRadius: { type: "number", minimum: 0 },
|
|
7290
|
+
cornerRadius: { type: "number", minimum: 0, description: "Live corner radius of a rect, polygon or star." },
|
|
7291
|
+
cornerKind: { type: "string", enum: VECTOR_CORNER_KINDS },
|
|
5555
7292
|
sides: { type: "integer", minimum: 3 },
|
|
5556
7293
|
points: { type: "integer", minimum: 3 },
|
|
5557
7294
|
innerRatio: { type: "number", minimum: 0, maximum: 1 },
|
|
@@ -5574,7 +7311,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5574
7311
|
run: (_ctx, params) => {
|
|
5575
7312
|
const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
|
|
5576
7313
|
const node = controller.getNode(id);
|
|
5577
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
7314
|
+
if (_optionalChain([node, 'optionalAccess', _232 => _232.type]) === "path" && !node.subpaths.length) {
|
|
5578
7315
|
controller.undo();
|
|
5579
7316
|
fail("The path data has no points.");
|
|
5580
7317
|
}
|
|
@@ -5597,6 +7334,76 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5597
7334
|
return { ok: true };
|
|
5598
7335
|
}
|
|
5599
7336
|
},
|
|
7337
|
+
{
|
|
7338
|
+
...base,
|
|
7339
|
+
id: `${prefix}setCorners`,
|
|
7340
|
+
label: "Corner Radius\u2026",
|
|
7341
|
+
keywords: ["round", "rounded", "corners", "radius", "fillet", "chamfer", "live corners"],
|
|
7342
|
+
description: "Sets the live corner radius (and optionally `kind`: round, inverted or chamfer) of `anchors`, else the selected points (direct tool), else every corner of `ids` / the selection. Radius 0 makes corners sharp; radii shrink to fit short sides. The anchors stay editable.",
|
|
7343
|
+
params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
|
|
7344
|
+
undoable: true,
|
|
7345
|
+
run: (_ctx, params) => {
|
|
7346
|
+
const refs = _optionalChain([params, 'optionalAccess', _233 => _233.anchors]) ? anchorsOf(params.anchors) : void 0;
|
|
7347
|
+
const target = { refs, ids: _optionalChain([params, 'optionalAccess', _234 => _234.ids]) ? ids(params) : void 0 };
|
|
7348
|
+
if (_optionalChain([params, 'optionalAccess', _235 => _235.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
|
|
7349
|
+
else if (_optionalChain([params, 'optionalAccess', _236 => _236.kind])) check(controller.setCornerKind(params.kind, target));
|
|
7350
|
+
else fail("Pass `radius` and / or `kind`.");
|
|
7351
|
+
return { ok: true };
|
|
7352
|
+
}
|
|
7353
|
+
},
|
|
7354
|
+
{
|
|
7355
|
+
...base,
|
|
7356
|
+
id: `${prefix}setWidthProfile`,
|
|
7357
|
+
label: "Width Profile\u2026",
|
|
7358
|
+
keywords: ["variable width", "taper", "tapered", "pressure", "profile", "brush", "calligraphy", "bulge", "thick", "thin"],
|
|
7359
|
+
description: "Gives the stroke of paths a variable width: a `preset` (`uniform` removes the profile), explicit `points` ({ t, left, right }: side scales along each subpath), or a parametric `taper` (startLength / endLength as fractions of the path, startWidth / endWidth 0\u20131, startEase / endEase 0\u20131) and / or `wave` (amount 0\u20131, cycles, phase in degrees). Profiled strokes draw as filled outlines; Outline Stroke bakes them.",
|
|
7360
|
+
params: {
|
|
7361
|
+
type: "object",
|
|
7362
|
+
properties: {
|
|
7363
|
+
ids: IDS,
|
|
7364
|
+
preset: { type: "string", enum: VECTOR_WIDTH_PRESET_IDS },
|
|
7365
|
+
points: WIDTH_POINTS,
|
|
7366
|
+
taper: {
|
|
7367
|
+
type: "object",
|
|
7368
|
+
properties: {
|
|
7369
|
+
startLength: { type: "number", minimum: 0, maximum: 1 },
|
|
7370
|
+
endLength: { type: "number", minimum: 0, maximum: 1 },
|
|
7371
|
+
startWidth: { type: "number", minimum: 0, maximum: 1 },
|
|
7372
|
+
endWidth: { type: "number", minimum: 0, maximum: 1 },
|
|
7373
|
+
startEase: { type: "number", minimum: 0, maximum: 1 },
|
|
7374
|
+
endEase: { type: "number", minimum: 0, maximum: 1 }
|
|
7375
|
+
}
|
|
7376
|
+
},
|
|
7377
|
+
wave: { type: "object", properties: { amount: { type: "number", minimum: 0, maximum: 1 }, cycles: { type: "number", exclusiveMinimum: 0 }, phase: { type: "number" } } }
|
|
7378
|
+
}
|
|
7379
|
+
},
|
|
7380
|
+
enabled: enabledFor,
|
|
7381
|
+
undoable: true,
|
|
7382
|
+
run: (_ctx, params) => {
|
|
7383
|
+
const target = { ids: ids(params) };
|
|
7384
|
+
if (_optionalChain([params, 'optionalAccess', _237 => _237.preset])) check(controller.setWidthProfile(params.preset, target));
|
|
7385
|
+
else if (_optionalChain([params, 'optionalAccess', _238 => _238.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
|
|
7386
|
+
else if (_optionalChain([params, 'optionalAccess', _239 => _239.taper]) || _optionalChain([params, 'optionalAccess', _240 => _240.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
|
|
7387
|
+
else fail("Pass `preset`, `points`, `taper` or `wave`.");
|
|
7388
|
+
const node = controller.getNode(target.ids[0]);
|
|
7389
|
+
return { ok: true, points: _optionalChain([node, 'optionalAccess', _241 => _241.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
|
|
7390
|
+
}
|
|
7391
|
+
},
|
|
7392
|
+
{
|
|
7393
|
+
...base,
|
|
7394
|
+
id: `${prefix}outlineStroke`,
|
|
7395
|
+
label: "Outline Stroke",
|
|
7396
|
+
keywords: ["expand", "stroke to path", "outline", "convert stroke"],
|
|
7397
|
+
description: "Turns the stroke of paths into filled shapes of the area it paints (alignment, caps, joins, dashes and arrowheads included). Returns the new ids.",
|
|
7398
|
+
params: { type: "object", properties: { ids: IDS } },
|
|
7399
|
+
enabled: enabledFor,
|
|
7400
|
+
undoable: true,
|
|
7401
|
+
run: (_ctx, params) => {
|
|
7402
|
+
const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _242 => _242.ids]) ? ids(params) : void 0 });
|
|
7403
|
+
if (!created.length) fail("No selected path has a stroke.");
|
|
7404
|
+
return { ids: created };
|
|
7405
|
+
}
|
|
7406
|
+
},
|
|
5600
7407
|
{
|
|
5601
7408
|
...base,
|
|
5602
7409
|
id: `${prefix}transform`,
|
|
@@ -5707,7 +7514,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5707
7514
|
enabled: enabledFor,
|
|
5708
7515
|
undoable: true,
|
|
5709
7516
|
run: (_ctx, params) => {
|
|
5710
|
-
const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess',
|
|
7517
|
+
const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _243 => _243.name]) });
|
|
5711
7518
|
if (!id) fail("Only nodes that share a parent can be grouped.");
|
|
5712
7519
|
return { id };
|
|
5713
7520
|
}
|
|
@@ -5735,7 +7542,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5735
7542
|
params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
|
|
5736
7543
|
undoable: true,
|
|
5737
7544
|
run: (_ctx, params) => {
|
|
5738
|
-
check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess',
|
|
7545
|
+
check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _244 => _244.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _245 => _245.keepShape]) }));
|
|
5739
7546
|
return { ok: true };
|
|
5740
7547
|
}
|
|
5741
7548
|
},
|
|
@@ -5790,7 +7597,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5790
7597
|
params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
|
|
5791
7598
|
undoable: true,
|
|
5792
7599
|
run: (_ctx, params) => {
|
|
5793
|
-
check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess',
|
|
7600
|
+
check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _246 => _246.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _247 => _247.tolerance]) }));
|
|
5794
7601
|
return { selection: controller.getState().selection };
|
|
5795
7602
|
}
|
|
5796
7603
|
},
|
|
@@ -5804,7 +7611,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5804
7611
|
params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
|
|
5805
7612
|
undoable: true,
|
|
5806
7613
|
run: (_ctx, params) => {
|
|
5807
|
-
check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess',
|
|
7614
|
+
check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _248 => _248.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _249 => _249.anchors]))));
|
|
5808
7615
|
return { ok: true };
|
|
5809
7616
|
}
|
|
5810
7617
|
},
|
|
@@ -5818,7 +7625,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5818
7625
|
enabled: enabledFor,
|
|
5819
7626
|
undoable: true,
|
|
5820
7627
|
run: (_ctx, params) => {
|
|
5821
|
-
check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess',
|
|
7628
|
+
check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _250 => _250.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _251 => _251.tolerance]) }));
|
|
5822
7629
|
return { ok: true };
|
|
5823
7630
|
}
|
|
5824
7631
|
},
|
|
@@ -5831,7 +7638,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5831
7638
|
params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
|
|
5832
7639
|
undoable: true,
|
|
5833
7640
|
run: (_ctx, params) => {
|
|
5834
|
-
check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess',
|
|
7641
|
+
check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _252 => _252.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _253 => _253.amount]), refs: _optionalChain([params, 'optionalAccess', _254 => _254.anchors]) }));
|
|
5835
7642
|
return { ok: true };
|
|
5836
7643
|
}
|
|
5837
7644
|
},
|
|
@@ -5844,14 +7651,14 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5844
7651
|
params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
|
|
5845
7652
|
undoable: true,
|
|
5846
7653
|
run: (_ctx, params) => {
|
|
5847
|
-
if (_optionalChain([params, 'optionalAccess',
|
|
7654
|
+
if (_optionalChain([params, 'optionalAccess', _255 => _255.nodeId]) !== void 0) {
|
|
5848
7655
|
if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
|
|
5849
7656
|
if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
|
|
5850
7657
|
const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
|
|
5851
7658
|
if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
|
|
5852
7659
|
return { ids: created };
|
|
5853
7660
|
}
|
|
5854
|
-
check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess',
|
|
7661
|
+
check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _256 => _256.anchors]))));
|
|
5855
7662
|
return { ids: controller.getState().selection };
|
|
5856
7663
|
}
|
|
5857
7664
|
},
|
|
@@ -5888,7 +7695,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5888
7695
|
id: `${prefix}setEditing`,
|
|
5889
7696
|
label: "Path Editing Options\u2026",
|
|
5890
7697
|
keywords: ["settings", "curvature", "pencil", "smoothing", "lazy mouse", "shapes", "eraser"],
|
|
5891
|
-
description: "Sets how editing behaves: `keepShapeOnDelete`, `curvatureMoves` (anchor drags bend like the Curvature tool), `pencilSmoothing` (lazy mouse, 0\u20131), `recognizeShapes` (pencil scribbles become lines, rectangles, ellipses, polygons) and `
|
|
7698
|
+
description: "Sets how editing behaves: `keepShapeOnDelete`, `curvatureMoves` (anchor drags bend like the Curvature tool), `pencilSmoothing` (lazy mouse, 0\u20131), `recognizeShapes` (pencil scribbles become lines, rectangles, ellipses, polygons), `eraserSize` and `pencilPressure` (`pen`: record pen pressure on pencil strokes as a width profile, `all`: any pointer reporting pressure, `off`).",
|
|
5892
7699
|
params: {
|
|
5893
7700
|
type: "object",
|
|
5894
7701
|
properties: {
|
|
@@ -5896,12 +7703,13 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5896
7703
|
curvatureMoves: { type: "boolean" },
|
|
5897
7704
|
pencilSmoothing: { type: "number", minimum: 0, maximum: 1 },
|
|
5898
7705
|
recognizeShapes: { type: "boolean" },
|
|
5899
|
-
eraserSize: { type: "number", exclusiveMinimum: 0 }
|
|
7706
|
+
eraserSize: { type: "number", exclusiveMinimum: 0 },
|
|
7707
|
+
pencilPressure: { type: "string", enum: ["pen", "all", "off"] }
|
|
5900
7708
|
}
|
|
5901
7709
|
},
|
|
5902
7710
|
run: (_ctx, params) => {
|
|
5903
7711
|
const patch = {};
|
|
5904
|
-
for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize"]) if (_optionalChain([params, 'optionalAccess',
|
|
7712
|
+
for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _257 => _257[key]]) !== void 0) patch[key] = params[key];
|
|
5905
7713
|
controller.setEditing(patch);
|
|
5906
7714
|
return { editing: controller.getState().editing };
|
|
5907
7715
|
}
|
|
@@ -5926,7 +7734,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
5926
7734
|
keywords: ["download", "save", "markup"],
|
|
5927
7735
|
description: "Returns the document as SVG markup.",
|
|
5928
7736
|
params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
|
|
5929
|
-
run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess',
|
|
7737
|
+
run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _258 => _258.precision]) }) })
|
|
5930
7738
|
},
|
|
5931
7739
|
{
|
|
5932
7740
|
...base,
|
|
@@ -6058,5 +7866,71 @@ function createVectorCommands(controller, options = {}) {
|
|
|
6058
7866
|
|
|
6059
7867
|
|
|
6060
7868
|
|
|
6061
|
-
|
|
7869
|
+
|
|
7870
|
+
|
|
7871
|
+
|
|
7872
|
+
|
|
7873
|
+
|
|
7874
|
+
|
|
7875
|
+
|
|
7876
|
+
|
|
7877
|
+
|
|
7878
|
+
|
|
7879
|
+
|
|
7880
|
+
|
|
7881
|
+
|
|
7882
|
+
|
|
7883
|
+
|
|
7884
|
+
|
|
7885
|
+
|
|
7886
|
+
|
|
7887
|
+
|
|
7888
|
+
|
|
7889
|
+
|
|
7890
|
+
|
|
7891
|
+
|
|
7892
|
+
|
|
7893
|
+
|
|
7894
|
+
|
|
7895
|
+
|
|
7896
|
+
|
|
7897
|
+
|
|
7898
|
+
|
|
7899
|
+
|
|
7900
|
+
|
|
7901
|
+
|
|
7902
|
+
|
|
7903
|
+
|
|
7904
|
+
|
|
7905
|
+
|
|
7906
|
+
|
|
7907
|
+
|
|
7908
|
+
|
|
7909
|
+
|
|
7910
|
+
|
|
7911
|
+
|
|
7912
|
+
|
|
7913
|
+
|
|
7914
|
+
|
|
7915
|
+
|
|
7916
|
+
|
|
7917
|
+
|
|
7918
|
+
|
|
7919
|
+
|
|
7920
|
+
|
|
7921
|
+
|
|
7922
|
+
|
|
7923
|
+
|
|
7924
|
+
|
|
7925
|
+
|
|
7926
|
+
|
|
7927
|
+
|
|
7928
|
+
|
|
7929
|
+
|
|
7930
|
+
|
|
7931
|
+
|
|
7932
|
+
|
|
7933
|
+
|
|
7934
|
+
|
|
7935
|
+
exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA2; exports.LazyMouse = LazyMouse; exports.VECTOR_ARROWHEADS = VECTOR_ARROWHEADS; exports.VECTOR_ARROWHEAD_LABELS = VECTOR_ARROWHEAD_LABELS; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertWidthPoint = insertWidthPoint; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mirrorWidthProfile = mirrorWidthProfile; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute;
|
|
6062
7936
|
//# sourceMappingURL=index.cjs.map
|