@pixerate/editor 0.100.0 → 0.101.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 +4 -0
- package/dist/animation-editor/index.cjs +39 -39
- package/dist/animation-editor/index.d.cts +1 -1
- package/dist/animation-editor/index.d.ts +1 -1
- package/dist/animation-editor/index.js +1 -1
- package/dist/{chunk-YGZNGDIT.js → chunk-63EFQCT5.js} +807 -73
- package/dist/chunk-63EFQCT5.js.map +1 -0
- package/dist/{chunk-JF5PXN7W.cjs → chunk-KANRAHTL.cjs} +838 -104
- package/dist/chunk-KANRAHTL.cjs.map +1 -0
- package/dist/{svg-C9r284x8.d.cts → svg-Bj1MQFh6.d.cts} +395 -2
- package/dist/{svg-MVKBqwD4.d.ts → svg-vme0PLNo.d.ts} +395 -2
- package/dist/vector/index.cjs +1352 -584
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +302 -197
- package/dist/vector/index.d.ts +302 -197
- package/dist/vector/index.js +854 -86
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/pixerate-vector/SKILL.md +52 -2
- package/skills/pixerate-vector/references/commands.md +21 -0
- package/dist/chunk-JF5PXN7W.cjs.map +0 -1
- package/dist/chunk-YGZNGDIT.js.map +0 -1
package/dist/vector/index.js
CHANGED
|
@@ -14,12 +14,19 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
KAPPA,
|
|
16
16
|
VECTOR_ARROWHEADS,
|
|
17
|
+
VECTOR_BLEND_MODES,
|
|
18
|
+
VECTOR_BLEND_MODE_LABELS,
|
|
17
19
|
VECTOR_BLEND_SPACINGS,
|
|
18
20
|
VECTOR_CORNER_KINDS,
|
|
19
21
|
VECTOR_EFFECT_DEFAULTS,
|
|
20
22
|
VECTOR_EFFECT_LABELS,
|
|
21
23
|
VECTOR_EFFECT_PARAM_SPECS,
|
|
22
24
|
VECTOR_EFFECT_TYPES,
|
|
25
|
+
VECTOR_FILTER_DEFAULTS,
|
|
26
|
+
VECTOR_FILTER_LABELS,
|
|
27
|
+
VECTOR_FILTER_MENU,
|
|
28
|
+
VECTOR_FILTER_PARAM_SPECS,
|
|
29
|
+
VECTOR_FILTER_TYPES,
|
|
23
30
|
VECTOR_GRADIENT_INTERPOLATIONS,
|
|
24
31
|
VECTOR_GRADIENT_MAX_PERIODS,
|
|
25
32
|
VECTOR_GRADIENT_MIN_STOPS,
|
|
@@ -29,6 +36,7 @@ import {
|
|
|
29
36
|
VECTOR_PATHFINDER_OPERATIONS,
|
|
30
37
|
VECTOR_PRESSURE_NOMINAL,
|
|
31
38
|
VECTOR_STROKE_GRADIENT_MODES,
|
|
39
|
+
VECTOR_STROKE_SETTING_KEYS,
|
|
32
40
|
VECTOR_TOOLS,
|
|
33
41
|
VECTOR_TRANSFORM_HANDLES,
|
|
34
42
|
VECTOR_WIDTH_PRESETS,
|
|
@@ -37,6 +45,7 @@ import {
|
|
|
37
45
|
VECTOR_WIDTH_PROFILE_MAX_SCALE,
|
|
38
46
|
VECTOR_WIDTH_SAME_T,
|
|
39
47
|
activeVectorEffects,
|
|
48
|
+
activeVectorFilters,
|
|
40
49
|
applyVectorCornersAttribute,
|
|
41
50
|
applyVectorEffects,
|
|
42
51
|
applyVectorGradientPatch,
|
|
@@ -60,6 +69,7 @@ import {
|
|
|
60
69
|
cornerSetback,
|
|
61
70
|
correspondMorphContours,
|
|
62
71
|
createVectorEffect,
|
|
72
|
+
createVectorFilter,
|
|
63
73
|
createVectorMorph,
|
|
64
74
|
dashIntervals,
|
|
65
75
|
defaultVectorGradient,
|
|
@@ -86,8 +96,11 @@ import {
|
|
|
86
96
|
hasStrokeExtras,
|
|
87
97
|
hasVectorAppearance,
|
|
88
98
|
hasVectorCorners,
|
|
99
|
+
hasVectorPaintStacks,
|
|
89
100
|
hitTestVectorNode,
|
|
90
101
|
hitTestVectorNodes,
|
|
102
|
+
hitTestVectorPaintLayer,
|
|
103
|
+
hitTestVectorPaintStack,
|
|
91
104
|
hitTestVectorStroke,
|
|
92
105
|
inferAnchorType,
|
|
93
106
|
insertAnchor,
|
|
@@ -102,7 +115,11 @@ import {
|
|
|
102
115
|
interpolateWidthProfiles,
|
|
103
116
|
isCentredVectorGradient,
|
|
104
117
|
isClosedBezierShape,
|
|
118
|
+
isDrawnVectorPaintLayer,
|
|
119
|
+
isPlainVectorPaintLayer,
|
|
105
120
|
isUniformWidthProfile,
|
|
121
|
+
isVectorBlendMode,
|
|
122
|
+
isVectorFilterType,
|
|
106
123
|
isVectorGradient,
|
|
107
124
|
isVectorLiveBoolean,
|
|
108
125
|
lineSubpath,
|
|
@@ -121,9 +138,13 @@ import {
|
|
|
121
138
|
moveVectorGradientStop,
|
|
122
139
|
nearestSegment,
|
|
123
140
|
nearestStrokePosition,
|
|
141
|
+
needsVectorPaintLayers,
|
|
142
|
+
newVectorStrokeLayer,
|
|
124
143
|
nextVectorCornerKind,
|
|
125
144
|
nodeMatrix,
|
|
126
145
|
normalizeVectorEffectParams,
|
|
146
|
+
normalizeVectorFilterParams,
|
|
147
|
+
normalizeVectorPaintStacks,
|
|
127
148
|
offsetPaths,
|
|
128
149
|
parseSvgTransform,
|
|
129
150
|
parseVectorColor,
|
|
@@ -153,7 +174,11 @@ import {
|
|
|
153
174
|
sampleVectorGradientAt,
|
|
154
175
|
sampleVectorPaintDefAt,
|
|
155
176
|
sanitizeVectorEffects,
|
|
177
|
+
sanitizeVectorFills,
|
|
178
|
+
sanitizeVectorFilters,
|
|
156
179
|
sanitizeVectorGradient,
|
|
180
|
+
sanitizeVectorStrokeSettings,
|
|
181
|
+
sanitizeVectorStrokes,
|
|
157
182
|
sanitizeWidthProfile,
|
|
158
183
|
setAnchorCorner,
|
|
159
184
|
setSubpathCorners,
|
|
@@ -185,6 +210,8 @@ import {
|
|
|
185
210
|
vectorAppearanceNodes,
|
|
186
211
|
vectorAppearanceOutline,
|
|
187
212
|
vectorAppearanceShapes,
|
|
213
|
+
vectorBlendModeCss,
|
|
214
|
+
vectorBlendModeFromCss,
|
|
188
215
|
vectorBoxMatrix,
|
|
189
216
|
vectorCornerAt,
|
|
190
217
|
vectorCorners,
|
|
@@ -196,6 +223,11 @@ import {
|
|
|
196
223
|
vectorEnvelopeFrame,
|
|
197
224
|
vectorEnvelopeMap,
|
|
198
225
|
vectorEnvelopeMesh,
|
|
226
|
+
vectorFills,
|
|
227
|
+
vectorFilterMarkup,
|
|
228
|
+
vectorFilterParamKeys,
|
|
229
|
+
vectorFilterReach,
|
|
230
|
+
vectorFilterSvg,
|
|
199
231
|
vectorGradientArea,
|
|
200
232
|
vectorGradientAspect,
|
|
201
233
|
vectorGradientFocalParam,
|
|
@@ -215,22 +247,30 @@ import {
|
|
|
215
247
|
vectorMeshOutline,
|
|
216
248
|
vectorNodeBounds,
|
|
217
249
|
vectorNodeEnvelope,
|
|
250
|
+
vectorNodeFilterSvg,
|
|
251
|
+
vectorNodeLocalPaintBounds,
|
|
218
252
|
vectorNodeRotation,
|
|
219
253
|
vectorNodeToBezier,
|
|
220
254
|
vectorOperandOperation,
|
|
255
|
+
vectorPaintLayerNodes,
|
|
221
256
|
vectorPaintToSvg,
|
|
222
257
|
vectorParentMatrix,
|
|
223
258
|
vectorPathData,
|
|
224
259
|
vectorPathLocalBounds,
|
|
225
260
|
vectorPathWorldBezier,
|
|
226
261
|
vectorRepeaterCopies,
|
|
262
|
+
vectorStacksHaveGradient,
|
|
227
263
|
vectorStrokeGradientDefs,
|
|
228
264
|
vectorStrokeLineArea,
|
|
229
265
|
vectorStrokeOutline,
|
|
230
266
|
vectorStrokeOutlinePath,
|
|
231
267
|
vectorStrokeParts,
|
|
232
268
|
vectorStrokeReach,
|
|
269
|
+
vectorStrokeSettings,
|
|
270
|
+
vectorStrokeStackReach,
|
|
233
271
|
vectorStrokeSvgParts,
|
|
272
|
+
vectorStrokes,
|
|
273
|
+
vectorTargetPaint,
|
|
234
274
|
vectorWorldMatrix,
|
|
235
275
|
walkVectorNodes,
|
|
236
276
|
widthProfileAt,
|
|
@@ -240,8 +280,11 @@ import {
|
|
|
240
280
|
widthProfilePresetId,
|
|
241
281
|
widthProfileToAttribute,
|
|
242
282
|
wigglePaths,
|
|
283
|
+
withVectorFills,
|
|
284
|
+
withVectorStrokes,
|
|
285
|
+
withVectorTargetPaint,
|
|
243
286
|
zigzagPaths
|
|
244
|
-
} from "../chunk-
|
|
287
|
+
} from "../chunk-63EFQCT5.js";
|
|
245
288
|
import {
|
|
246
289
|
bezierPathBounds,
|
|
247
290
|
bezierPathToSvg,
|
|
@@ -322,19 +365,19 @@ var HANDLE_HIT = 6;
|
|
|
322
365
|
var BAR_HIT = 5;
|
|
323
366
|
var fmt = (n) => Math.round(n * 100) / 100;
|
|
324
367
|
var apply = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
|
|
325
|
-
function vectorGradientTargetPath(doc, selection, target) {
|
|
368
|
+
function vectorGradientTargetPath(doc, selection, target, layer = null) {
|
|
326
369
|
for (const id of selection) {
|
|
327
370
|
const node = findVectorNode(doc.nodes, id);
|
|
328
371
|
if (!node) continue;
|
|
329
|
-
const path = collectPathNodes(node).find((p) => isVectorGradient(p
|
|
372
|
+
const path = collectPathNodes(node).find((p) => isVectorGradient(vectorTargetPaint(p, target, layer)));
|
|
330
373
|
if (path) return path;
|
|
331
374
|
}
|
|
332
375
|
return null;
|
|
333
376
|
}
|
|
334
|
-
function getVectorGradientAnnotator(doc, selection, viewport, target) {
|
|
335
|
-
const path = vectorGradientTargetPath(doc, selection, target);
|
|
377
|
+
function getVectorGradientAnnotator(doc, selection, viewport, target, layer = null) {
|
|
378
|
+
const path = vectorGradientTargetPath(doc, selection, target, layer);
|
|
336
379
|
if (!path) return null;
|
|
337
|
-
const gradient = path
|
|
380
|
+
const gradient = vectorTargetPaint(path, target, layer);
|
|
338
381
|
const world = vectorWorldMatrix(doc.nodes, path.id) ?? IDENTITY_MATRIX;
|
|
339
382
|
const screen = multiplyMatrices(world, [viewport.zoom, 0, 0, viewport.zoom, viewport.x, viewport.y]);
|
|
340
383
|
const bounds = vectorPathLocalBounds(path);
|
|
@@ -2655,7 +2698,9 @@ var VECTOR_STYLE_KEYS = [
|
|
|
2655
2698
|
"arrowAlign",
|
|
2656
2699
|
"startArrowScale",
|
|
2657
2700
|
"endArrowScale",
|
|
2658
|
-
"strokeWidthProfile"
|
|
2701
|
+
"strokeWidthProfile",
|
|
2702
|
+
"fills",
|
|
2703
|
+
"strokes"
|
|
2659
2704
|
];
|
|
2660
2705
|
var VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
|
|
2661
2706
|
var DEFAULT_DEFAULTS = {
|
|
@@ -2975,6 +3020,24 @@ var VECTOR_PATHFINDER_LABELS = {
|
|
|
2975
3020
|
outline: "Outline",
|
|
2976
3021
|
minusBack: "Minus Back"
|
|
2977
3022
|
};
|
|
3023
|
+
function normalizeVectorTree(nodes, prefer) {
|
|
3024
|
+
let changed = false;
|
|
3025
|
+
const out = nodes.map((n) => {
|
|
3026
|
+
let next = n.type === "path" ? normalizeVectorPaintStacks(n, prefer) : n;
|
|
3027
|
+
if (next.type === "group") {
|
|
3028
|
+
const children = normalizeVectorTree(next.children, prefer);
|
|
3029
|
+
if (children !== next.children) next = { ...next, children };
|
|
3030
|
+
}
|
|
3031
|
+
if (next !== n) changed = true;
|
|
3032
|
+
return next;
|
|
3033
|
+
});
|
|
3034
|
+
return changed ? out : nodes;
|
|
3035
|
+
}
|
|
3036
|
+
function loadedDocument(doc) {
|
|
3037
|
+
const copy = cloneVectorDocument(doc);
|
|
3038
|
+
const nodes = normalizeVectorTree(copy.nodes, "stack");
|
|
3039
|
+
return nodes === copy.nodes ? copy : { ...copy, nodes };
|
|
3040
|
+
}
|
|
2978
3041
|
function mapTree(nodes, fn) {
|
|
2979
3042
|
let changed = false;
|
|
2980
3043
|
const out = [];
|
|
@@ -3054,7 +3117,7 @@ var VectorEditorController = class {
|
|
|
3054
3117
|
blendStart = null;
|
|
3055
3118
|
/** Options new blends get (the toolbars edit them along with the selected blend's). */
|
|
3056
3119
|
blendDefaults = {};
|
|
3057
|
-
gradientEdit = { target: "fill", stop: null };
|
|
3120
|
+
gradientEdit = { target: "fill", stop: null, layer: null };
|
|
3058
3121
|
freeDistort = false;
|
|
3059
3122
|
/** What the highlight shows, to skip notifying when the pointer stays over the same face. */
|
|
3060
3123
|
builderHoverKey = null;
|
|
@@ -3069,7 +3132,7 @@ var VectorEditorController = class {
|
|
|
3069
3132
|
state;
|
|
3070
3133
|
constructor(options = {}) {
|
|
3071
3134
|
this.history = options.history ?? new HistoryManager();
|
|
3072
|
-
this.doc = options.document ?
|
|
3135
|
+
this.doc = options.document ? loadedDocument(options.document) : { width: 800, height: 600, nodes: [] };
|
|
3073
3136
|
this.doc = this.refreshed(this.doc);
|
|
3074
3137
|
this.tool = options.tool ?? "select";
|
|
3075
3138
|
this.defaults = { ...DEFAULT_DEFAULTS, ...options.defaults ?? {} };
|
|
@@ -3161,7 +3224,7 @@ var VectorEditorController = class {
|
|
|
3161
3224
|
}
|
|
3162
3225
|
/** `doc` with its blends' steps brought up to date (the same object when they already are). */
|
|
3163
3226
|
refreshed(doc) {
|
|
3164
|
-
const nodes = refreshVectorBlends(doc.nodes);
|
|
3227
|
+
const nodes = refreshVectorBlends(normalizeVectorTree(doc.nodes, "shorthand"));
|
|
3165
3228
|
return nodes === doc.nodes ? doc : { ...doc, nodes };
|
|
3166
3229
|
}
|
|
3167
3230
|
notify(docChanged = false) {
|
|
@@ -3480,7 +3543,7 @@ var VectorEditorController = class {
|
|
|
3480
3543
|
/** Replaces the document. Undoable unless `{ history: false }` (then history is cleared). */
|
|
3481
3544
|
setDocument(doc, options = {}) {
|
|
3482
3545
|
this.cancelDrag();
|
|
3483
|
-
const next =
|
|
3546
|
+
const next = loadedDocument(doc);
|
|
3484
3547
|
if (options.history === false) {
|
|
3485
3548
|
this.doc = next;
|
|
3486
3549
|
this.selection = [];
|
|
@@ -3594,7 +3657,7 @@ var VectorEditorController = class {
|
|
|
3594
3657
|
for (const key of VECTOR_STYLE_KEYS) {
|
|
3595
3658
|
if (input[key] !== void 0) own[key] = input[key];
|
|
3596
3659
|
}
|
|
3597
|
-
return { id: this.newId(), type: "path", name: input.name ?? names[input.kind], ...style,
|
|
3660
|
+
return withStyle({ id: this.newId(), type: "path", name: input.name ?? names[input.kind], ...style, subpaths: [subpath] }, own);
|
|
3598
3661
|
}
|
|
3599
3662
|
/** Adds a shape drawn with the current defaults. Returns its id. */
|
|
3600
3663
|
addShape(input, options = {}) {
|
|
@@ -3604,7 +3667,8 @@ var VectorEditorController = class {
|
|
|
3604
3667
|
/** Adds a path from SVG path data. Returns its id. */
|
|
3605
3668
|
addPath(d, style = {}, options = {}) {
|
|
3606
3669
|
const subpaths = bezierToSubpaths(d);
|
|
3607
|
-
const
|
|
3670
|
+
const { name, ...own } = style;
|
|
3671
|
+
const node = withStyle({ id: this.newId(), type: "path", name: name ?? "Path", ...this.styleFor(false), subpaths }, own);
|
|
3608
3672
|
return this.addNode(node, { label: "Add Path", ...options });
|
|
3609
3673
|
}
|
|
3610
3674
|
styleFor(open) {
|
|
@@ -3617,7 +3681,10 @@ var VectorEditorController = class {
|
|
|
3617
3681
|
if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
|
|
3618
3682
|
for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
|
|
3619
3683
|
if (d.strokeWidthProfile?.length) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
|
|
3620
|
-
|
|
3684
|
+
let styled = out;
|
|
3685
|
+
if (Array.isArray(d.fills)) styled = withVectorFills(styled, open ? [] : d.fills);
|
|
3686
|
+
if (Array.isArray(d.strokes)) styled = withVectorStrokes(styled, d.strokes);
|
|
3687
|
+
return styled;
|
|
3621
3688
|
}
|
|
3622
3689
|
/** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
|
|
3623
3690
|
updateNode(id, patch, options = {}) {
|
|
@@ -3666,9 +3733,9 @@ var VectorEditorController = class {
|
|
|
3666
3733
|
// Gradients
|
|
3667
3734
|
// -------------------------------------------------------------------------
|
|
3668
3735
|
/** Which paint (fill or stroke) the gradient tool and editor work on. */
|
|
3669
|
-
setGradientTarget(target) {
|
|
3670
|
-
if (target === this.gradientEdit.target) return;
|
|
3671
|
-
this.gradientEdit = { target, stop: null };
|
|
3736
|
+
setGradientTarget(target, layer = null) {
|
|
3737
|
+
if (target === this.gradientEdit.target && layer === this.gradientEdit.layer) return;
|
|
3738
|
+
this.gradientEdit = { target, stop: null, layer };
|
|
3672
3739
|
this.notify();
|
|
3673
3740
|
}
|
|
3674
3741
|
/** Selects a stop of the gradient being edited (index into its sorted stops), or none. */
|
|
@@ -3679,7 +3746,7 @@ var VectorEditorController = class {
|
|
|
3679
3746
|
}
|
|
3680
3747
|
/** The gradient tool's annotator for the selection (screen pixels), or null when its paint is not a gradient. */
|
|
3681
3748
|
getGradientAnnotator() {
|
|
3682
|
-
return getVectorGradientAnnotator(this.doc, this.selection, this.viewport, this.gradientEdit.target);
|
|
3749
|
+
return getVectorGradientAnnotator(this.doc, this.selection, this.viewport, this.gradientEdit.target, this.gradientEdit.layer);
|
|
3683
3750
|
}
|
|
3684
3751
|
/** Unlocked paths of `ids` (default: the selection). */
|
|
3685
3752
|
gradientPaths(ids = this.selection) {
|
|
@@ -3699,32 +3766,35 @@ var VectorEditorController = class {
|
|
|
3699
3766
|
*/
|
|
3700
3767
|
setGradient(patch, options = {}) {
|
|
3701
3768
|
const target = options.target ?? this.gradientEdit.target;
|
|
3769
|
+
const layer = options.layer !== void 0 ? options.layer : options.target === void 0 ? this.gradientEdit.layer : null;
|
|
3702
3770
|
const paths = this.gradientPaths(options.ids ?? this.selection);
|
|
3703
3771
|
if (!paths.length) return { ok: false, message: "Nothing is selected." };
|
|
3704
3772
|
const next = /* @__PURE__ */ new Map();
|
|
3773
|
+
let first = null;
|
|
3705
3774
|
for (const path of paths) {
|
|
3706
|
-
const paint = path
|
|
3775
|
+
const paint = vectorTargetPaint(path, target, layer);
|
|
3707
3776
|
const base = isVectorGradient(paint) ? paint : defaultVectorGradient(patch.type ?? "linear", paint);
|
|
3708
3777
|
const g = applyVectorGradientPatch(base, patch);
|
|
3709
3778
|
if (!g) return { ok: false, message: "That is not a valid gradient: it needs at least one stop with an offset and a colour." };
|
|
3710
|
-
|
|
3779
|
+
first ??= g;
|
|
3780
|
+
next.set(path.id, withVectorTargetPaint(path, target, layer, g));
|
|
3711
3781
|
}
|
|
3712
3782
|
this.change(options.label ?? "Gradient", () => this.setDoc(mapTree(this.doc.nodes, (n) => next.get(n.id) ?? n)), options.merge);
|
|
3713
|
-
const stops =
|
|
3783
|
+
const stops = first?.stops.length ?? 0;
|
|
3714
3784
|
if (this.gradientEdit.stop !== null && this.gradientEdit.stop >= stops) this.gradientEdit = { ...this.gradientEdit, stop: null };
|
|
3715
3785
|
return { ok: true };
|
|
3716
3786
|
}
|
|
3717
3787
|
/** Applies `fn` to every target gradient of the selection (paths whose paint is not a gradient are skipped). */
|
|
3718
3788
|
editGradients(label, fn, merge) {
|
|
3719
|
-
const target = this.gradientEdit
|
|
3720
|
-
const paths = this.gradientPaths().filter((p) => isVectorGradient(p
|
|
3789
|
+
const { target, layer } = this.gradientEdit;
|
|
3790
|
+
const paths = this.gradientPaths().filter((p) => isVectorGradient(vectorTargetPaint(p, target, layer)));
|
|
3721
3791
|
if (!paths.length) return { ok: false, message: "The selection has no gradient." };
|
|
3722
3792
|
const next = /* @__PURE__ */ new Map();
|
|
3723
3793
|
for (const path of paths) {
|
|
3724
|
-
const g = path
|
|
3794
|
+
const g = vectorTargetPaint(path, target, layer);
|
|
3725
3795
|
const stops = fn(sortedVectorGradientStops(g));
|
|
3726
3796
|
if (!stops) return { ok: false, message: "A gradient keeps at least two stops." };
|
|
3727
|
-
next.set(path.id,
|
|
3797
|
+
next.set(path.id, withVectorTargetPaint(path, target, layer, { ...g, stops }));
|
|
3728
3798
|
}
|
|
3729
3799
|
this.change(label, () => this.setDoc(mapTree(this.doc.nodes, (n) => next.get(n.id) ?? n)), merge);
|
|
3730
3800
|
return { ok: true };
|
|
@@ -3763,10 +3833,10 @@ var VectorEditorController = class {
|
|
|
3763
3833
|
/** Adds a stop at `offset` (painting what the gradient paints there) to the selection's gradients and selects it. */
|
|
3764
3834
|
addGradientStop(offset) {
|
|
3765
3835
|
let index = 0;
|
|
3766
|
-
const target = this.gradientEdit
|
|
3836
|
+
const { target, layer } = this.gradientEdit;
|
|
3767
3837
|
const result = this.editGradients("Add Gradient Stop", (stops) => {
|
|
3768
|
-
const path = this.gradientPaths().find((p) => isVectorGradient(p
|
|
3769
|
-
const r = insertVectorGradientStop({ ...path
|
|
3838
|
+
const path = this.gradientPaths().find((p) => isVectorGradient(vectorTargetPaint(p, target, layer)));
|
|
3839
|
+
const r = insertVectorGradientStop({ ...vectorTargetPaint(path, target, layer), stops }, offset);
|
|
3770
3840
|
index = r.index;
|
|
3771
3841
|
return r.stops;
|
|
3772
3842
|
});
|
|
@@ -3785,7 +3855,8 @@ var VectorEditorController = class {
|
|
|
3785
3855
|
}
|
|
3786
3856
|
/** `doc` with path `id`'s `target` paint replaced. */
|
|
3787
3857
|
withGradient(doc, id, target, g) {
|
|
3788
|
-
|
|
3858
|
+
const layer = this.gradientEdit.layer;
|
|
3859
|
+
return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === id && n.type === "path" ? withVectorTargetPaint(n, target, layer, g) : n) };
|
|
3789
3860
|
}
|
|
3790
3861
|
downGradient(screen, p, input) {
|
|
3791
3862
|
const before = this.snapshot();
|
|
@@ -3830,7 +3901,7 @@ var VectorEditorController = class {
|
|
|
3830
3901
|
for (const id2 of drag.targets) {
|
|
3831
3902
|
const node2 = findVectorNode(base.nodes, id2);
|
|
3832
3903
|
if (!node2) continue;
|
|
3833
|
-
const paint = node2
|
|
3904
|
+
const paint = vectorTargetPaint(node2, target, this.gradientEdit.layer);
|
|
3834
3905
|
const g2 = isVectorGradient(paint) ? paint : defaultVectorGradient("linear", paint);
|
|
3835
3906
|
doc = this.withGradient(doc, id2, target, setVectorGradientVector(g2, vectorPathLocalBounds(node2), this.toLocal(id2, drag.startDoc, base), this.toLocal(id2, end, base)));
|
|
3836
3907
|
}
|
|
@@ -3840,7 +3911,7 @@ var VectorEditorController = class {
|
|
|
3840
3911
|
}
|
|
3841
3912
|
const id = drag.nodeId;
|
|
3842
3913
|
const node = findVectorNode(base.nodes, id);
|
|
3843
|
-
const g = node
|
|
3914
|
+
const g = vectorTargetPaint(node, target, this.gradientEdit.layer);
|
|
3844
3915
|
const bounds = vectorPathLocalBounds(node);
|
|
3845
3916
|
const local = (q) => this.toLocal(id, q, base);
|
|
3846
3917
|
const part = drag.part;
|
|
@@ -4067,39 +4138,35 @@ var VectorEditorController = class {
|
|
|
4067
4138
|
*/
|
|
4068
4139
|
outlineStroke(options = {}) {
|
|
4069
4140
|
const roots = options.ids ?? this.selectedRoots();
|
|
4141
|
+
const strokesOf = (p) => vectorPaintLayerNodes(p).filter((l) => l.kind === "stroke" && l.drawn && (l.node.strokeWidth ?? 1) > 0);
|
|
4070
4142
|
const paths = [];
|
|
4071
4143
|
for (const id of roots) {
|
|
4072
4144
|
const node = this.getNode(id);
|
|
4073
4145
|
if (node) {
|
|
4074
|
-
for (const p of collectPathNodes(node)) if (!p.locked &&
|
|
4146
|
+
for (const p of collectPathNodes(node)) if (!p.locked && strokesOf(p).length) paths.push(p);
|
|
4075
4147
|
}
|
|
4076
4148
|
}
|
|
4077
4149
|
if (!paths.length) return [];
|
|
4078
4150
|
const created = [];
|
|
4079
4151
|
const replace = /* @__PURE__ */ new Map();
|
|
4080
4152
|
for (const p of paths) {
|
|
4081
|
-
const
|
|
4082
|
-
|
|
4083
|
-
const
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
} = cloneVectorNode(p);
|
|
4099
|
-
const id = this.newId();
|
|
4100
|
-
const strokeNode = { ...rest, id, name: `${p.name ?? "Path"} Stroke`, subpaths: outline, fill: stroke ?? null, stroke: null, fillRule: "nonzero" };
|
|
4101
|
-
const list = hasPaint(p.fill) ? [{ ...rest, ...effects ? { effects } : {}, stroke: null }, strokeNode] : [strokeNode];
|
|
4102
|
-
created.push(id);
|
|
4153
|
+
const { effects, ...rest } = cloneVectorNode(p);
|
|
4154
|
+
const unstroked = withVectorStrokes(rest, []);
|
|
4155
|
+
for (const key of VECTOR_STROKE_SETTING_KEYS) delete unstroked[key];
|
|
4156
|
+
const list = vectorFills(p).length ? [{ ...unstroked, ...effects ? { effects } : {} }] : [];
|
|
4157
|
+
const layers = strokesOf(p);
|
|
4158
|
+
for (const l of layers) {
|
|
4159
|
+
const outline = bezierToSubpaths(vectorStrokeOutlinePath(vectorAppearanceOutline(p), l.node));
|
|
4160
|
+
if (!outline.length) continue;
|
|
4161
|
+
const id = this.newId();
|
|
4162
|
+
const { strokes: _s, fills: _f, ...plain2 } = unstroked;
|
|
4163
|
+
const name = layers.length > 1 ? `${p.name ?? "Path"} Stroke ${l.index + 1}` : `${p.name ?? "Path"} Stroke`;
|
|
4164
|
+
const fill = { paint: l.layer.paint };
|
|
4165
|
+
if (l.layer.opacity !== void 0) fill.opacity = l.layer.opacity;
|
|
4166
|
+
if (l.layer.blendMode) fill.blendMode = l.layer.blendMode;
|
|
4167
|
+
list.push(withVectorFills({ ...plain2, id, name, subpaths: outline, stroke: null, fillRule: "nonzero" }, [fill]));
|
|
4168
|
+
created.push(id);
|
|
4169
|
+
}
|
|
4103
4170
|
replace.set(p.id, list);
|
|
4104
4171
|
}
|
|
4105
4172
|
if (!created.length) return [];
|
|
@@ -4114,6 +4181,229 @@ var VectorEditorController = class {
|
|
|
4114
4181
|
return created;
|
|
4115
4182
|
}
|
|
4116
4183
|
// -------------------------------------------------------------------------
|
|
4184
|
+
// Fill and stroke stacks, blend modes, filters
|
|
4185
|
+
// -------------------------------------------------------------------------
|
|
4186
|
+
/** Paths of the selected (or given) nodes whose stacks can be edited (groups give every path inside; blend steps are left out). */
|
|
4187
|
+
stackTargets(ids) {
|
|
4188
|
+
const list = ids ?? this.selection;
|
|
4189
|
+
if (!list.length) return "Nothing is selected.";
|
|
4190
|
+
const out = /* @__PURE__ */ new Map();
|
|
4191
|
+
for (const id of list) {
|
|
4192
|
+
const node = this.getNode(id);
|
|
4193
|
+
if (!node) return `There is no node "${id}".`;
|
|
4194
|
+
for (const p of collectPathNodes(node)) if (!p.blendStep) out.set(p.id, p);
|
|
4195
|
+
}
|
|
4196
|
+
if (!out.size) return "The selection has no paths (fills and strokes belong to paths).";
|
|
4197
|
+
return [...out.values()];
|
|
4198
|
+
}
|
|
4199
|
+
/** Applies `fn` to the `kind` stack of every target path as one undo step (an error string from any path cancels it). */
|
|
4200
|
+
editStacks(kind, ids, label, fn, merge) {
|
|
4201
|
+
const targets = this.stackTargets(ids);
|
|
4202
|
+
if (typeof targets === "string") return { ok: false, message: targets };
|
|
4203
|
+
const next = /* @__PURE__ */ new Map();
|
|
4204
|
+
for (const path of targets) {
|
|
4205
|
+
const stack = kind === "fill" ? vectorFills(path) : vectorStrokes(path);
|
|
4206
|
+
const result = fn(stack.map((l) => ({ ...l })), path);
|
|
4207
|
+
if (typeof result === "string") return { ok: false, message: result };
|
|
4208
|
+
next.set(path.id, kind === "fill" ? withVectorFills(path, result) : withVectorStrokes(path, result));
|
|
4209
|
+
}
|
|
4210
|
+
this.change(label, () => this.setDoc(mapTree(this.doc.nodes, (n) => next.get(n.id) ?? n)), merge);
|
|
4211
|
+
return { ok: true };
|
|
4212
|
+
}
|
|
4213
|
+
/**
|
|
4214
|
+
* Reads a layer patch from untrusted input: paint, opacity, blend mode, visibility and (strokes)
|
|
4215
|
+
* the stroke settings, where `null` removes a setting. Returns an error string for invalid values.
|
|
4216
|
+
*/
|
|
4217
|
+
layerPatch(kind, patch) {
|
|
4218
|
+
const set = {};
|
|
4219
|
+
const unset = [];
|
|
4220
|
+
if ("paint" in patch) {
|
|
4221
|
+
const paint = patch.paint;
|
|
4222
|
+
if (paint === null || typeof paint === "string" && paint.trim()) set.paint = paint === null ? null : paint.trim();
|
|
4223
|
+
else {
|
|
4224
|
+
const g = sanitizeVectorGradient(paint);
|
|
4225
|
+
if (!g) return "paint must be a CSS colour, a gradient { type, stops: [{ offset, color }] } or null.";
|
|
4226
|
+
set.paint = g;
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
if (patch.opacity !== void 0) {
|
|
4230
|
+
if (typeof patch.opacity !== "number" || !Number.isFinite(patch.opacity)) return "opacity must be a number from 0 to 1.";
|
|
4231
|
+
const o = Math.min(1, Math.max(0, patch.opacity));
|
|
4232
|
+
if (o < 1) set.opacity = o;
|
|
4233
|
+
else unset.push("opacity");
|
|
4234
|
+
}
|
|
4235
|
+
if (patch.blendMode !== void 0) {
|
|
4236
|
+
if (!isVectorBlendMode(patch.blendMode)) return `Unknown blend mode "${String(patch.blendMode)}". Use one of: ${VECTOR_BLEND_MODES.join(", ")}.`;
|
|
4237
|
+
if (patch.blendMode !== "normal") set.blendMode = patch.blendMode;
|
|
4238
|
+
else unset.push("blendMode");
|
|
4239
|
+
}
|
|
4240
|
+
if (patch.visible !== void 0) {
|
|
4241
|
+
if (typeof patch.visible !== "boolean") return "visible must be true or false.";
|
|
4242
|
+
if (patch.visible) unset.push("visible");
|
|
4243
|
+
else set.visible = false;
|
|
4244
|
+
}
|
|
4245
|
+
if (kind === "stroke") {
|
|
4246
|
+
const settings = sanitizeVectorStrokeSettings(patch);
|
|
4247
|
+
for (const key of VECTOR_STROKE_SETTING_KEYS) {
|
|
4248
|
+
if (!(key in patch) || patch[key] === void 0) continue;
|
|
4249
|
+
if (patch[key] === null || key === "strokeDasharray" && Array.isArray(patch[key]) && !patch[key].length) unset.push(key);
|
|
4250
|
+
else if (settings[key] === void 0) return `Invalid ${key}: ${JSON.stringify(patch[key])}.`;
|
|
4251
|
+
else set[key] = settings[key];
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
return { set, unset };
|
|
4255
|
+
}
|
|
4256
|
+
/**
|
|
4257
|
+
* Adds a fill or stroke layer to the selected (or given) paths (groups: every path inside), at
|
|
4258
|
+
* the top of the stack or at `index` (0 is the bottom). A new stroke takes the settings of the
|
|
4259
|
+
* stack's top stroke unless `layer` gives them. One undo step.
|
|
4260
|
+
*/
|
|
4261
|
+
addPaintLayer(kind, layer = {}, options = {}) {
|
|
4262
|
+
const patch = this.layerPatch(kind, layer);
|
|
4263
|
+
if (typeof patch === "string") return { ok: false, message: patch };
|
|
4264
|
+
return this.editStacks(kind, options.ids, kind === "fill" ? "Add Fill" : "Add Stroke", (stack, path) => {
|
|
4265
|
+
const base = kind === "fill" ? { paint: "#808080" } : newVectorStrokeLayer(path, {});
|
|
4266
|
+
const added = { ...base, ...patch.set };
|
|
4267
|
+
for (const key of patch.unset) delete added[key];
|
|
4268
|
+
const at = options.index === void 0 ? stack.length : Math.min(stack.length, Math.max(0, Math.round(options.index)));
|
|
4269
|
+
return [...stack.slice(0, at), added, ...stack.slice(at)];
|
|
4270
|
+
});
|
|
4271
|
+
}
|
|
4272
|
+
/**
|
|
4273
|
+
* Changes layer `index` (0 is the bottom) of the fill or stroke stack of the selected (or given)
|
|
4274
|
+
* paths: paint, opacity, blend mode, visibility and, for strokes, any stroke setting (`null`
|
|
4275
|
+
* removes it). Fails, changing nothing, when a path has no layer `index`. With `merge` (true, or
|
|
4276
|
+
* a key), consecutive edits of the layer fold into one undo step (a slider drag).
|
|
4277
|
+
*/
|
|
4278
|
+
updatePaintLayer(kind, index, patch, options = {}) {
|
|
4279
|
+
const parsed = this.layerPatch(kind, patch);
|
|
4280
|
+
if (typeof parsed === "string") return { ok: false, message: parsed };
|
|
4281
|
+
const merge = options.merge === true ? `paint-layer:${kind}:${index}` : options.merge || void 0;
|
|
4282
|
+
return this.editStacks(
|
|
4283
|
+
kind,
|
|
4284
|
+
options.ids,
|
|
4285
|
+
kind === "fill" ? "Edit Fill" : "Edit Stroke",
|
|
4286
|
+
(stack, path) => {
|
|
4287
|
+
if (!stack[index]) return `Path "${path.id}" has no ${kind} ${index} (it has ${stack.length}; 0 is the bottom).`;
|
|
4288
|
+
const layer = { ...stack[index], ...parsed.set };
|
|
4289
|
+
for (const key of parsed.unset) delete layer[key];
|
|
4290
|
+
return stack.map((l, i) => i === index ? layer : l);
|
|
4291
|
+
},
|
|
4292
|
+
merge
|
|
4293
|
+
);
|
|
4294
|
+
}
|
|
4295
|
+
/** Removes layer `index` of the fill or stroke stack of the selected (or given) paths. */
|
|
4296
|
+
removePaintLayer(kind, index, options = {}) {
|
|
4297
|
+
return this.editStacks(
|
|
4298
|
+
kind,
|
|
4299
|
+
options.ids,
|
|
4300
|
+
kind === "fill" ? "Remove Fill" : "Remove Stroke",
|
|
4301
|
+
(stack, path) => stack[index] ? stack.filter((_, i) => i !== index) : `Path "${path.id}" has no ${kind} ${index} (it has ${stack.length}; 0 is the bottom).`
|
|
4302
|
+
);
|
|
4303
|
+
}
|
|
4304
|
+
/** Moves layer `from` of the fill or stroke stack to position `to` (0 is the bottom) on the selected (or given) paths. */
|
|
4305
|
+
movePaintLayer(kind, from, to, options = {}) {
|
|
4306
|
+
return this.editStacks(kind, options.ids, kind === "fill" ? "Reorder Fills" : "Reorder Strokes", (stack, path) => {
|
|
4307
|
+
if (!stack[from]) return `Path "${path.id}" has no ${kind} ${from} (it has ${stack.length}; 0 is the bottom).`;
|
|
4308
|
+
const out = stack.slice();
|
|
4309
|
+
const [layer] = out.splice(from, 1);
|
|
4310
|
+
out.splice(Math.min(out.length, Math.max(0, Math.round(to))), 0, layer);
|
|
4311
|
+
return out;
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4314
|
+
/** Replaces the whole fill or stroke stack of the selected (or given) paths (malformed layers are dropped). */
|
|
4315
|
+
setPaintLayers(kind, layers, options = {}) {
|
|
4316
|
+
const clean = kind === "fill" ? sanitizeVectorFills(layers) : sanitizeVectorStrokes(layers);
|
|
4317
|
+
return this.editStacks(kind, options.ids, kind === "fill" ? "Set Fills" : "Set Strokes", () => clean);
|
|
4318
|
+
}
|
|
4319
|
+
/** Sets the blend mode of the selected (or given) nodes (paths or groups); `normal` removes it. */
|
|
4320
|
+
setBlendMode(mode, options = {}) {
|
|
4321
|
+
if (!isVectorBlendMode(mode)) return { ok: false, message: `Unknown blend mode "${String(mode)}". Use one of: ${VECTOR_BLEND_MODES.join(", ")}.` };
|
|
4322
|
+
const ids = options.ids ?? this.selectedRoots();
|
|
4323
|
+
if (!ids.length) return { ok: false, message: "Nothing is selected." };
|
|
4324
|
+
for (const id of ids) if (!this.getNode(id)) return { ok: false, message: `There is no node "${id}".` };
|
|
4325
|
+
const set = new Set(ids);
|
|
4326
|
+
this.change(
|
|
4327
|
+
"Blend Mode",
|
|
4328
|
+
() => this.setDoc(
|
|
4329
|
+
mapTree(this.doc.nodes, (n) => {
|
|
4330
|
+
if (!set.has(n.id) || (n.blendMode ?? "normal") === mode) return n;
|
|
4331
|
+
const { blendMode: _b, ...rest } = n;
|
|
4332
|
+
return mode === "normal" ? rest : { ...rest, blendMode: mode };
|
|
4333
|
+
})
|
|
4334
|
+
)
|
|
4335
|
+
);
|
|
4336
|
+
return { ok: true };
|
|
4337
|
+
}
|
|
4338
|
+
/**
|
|
4339
|
+
* Adds a filter of `type` to the selected (or given) nodes, at the end of each stack (or at
|
|
4340
|
+
* `index`), with default parameters merged with `params`. One undo step; returns the new filter
|
|
4341
|
+
* ids (one per node, in order), empty for an unknown type or nothing selected.
|
|
4342
|
+
*/
|
|
4343
|
+
addFilter(type, options = {}) {
|
|
4344
|
+
if (!isVectorFilterType(type)) return [];
|
|
4345
|
+
const targets = this.effectTargets(options.ids);
|
|
4346
|
+
if (!targets.length) return [];
|
|
4347
|
+
const created = [];
|
|
4348
|
+
const next = /* @__PURE__ */ new Map();
|
|
4349
|
+
for (const node of targets) {
|
|
4350
|
+
const list = node.filters ?? [];
|
|
4351
|
+
let k = list.length + 1;
|
|
4352
|
+
while (list.some((f) => f.id === `filter${k}`)) k++;
|
|
4353
|
+
const filter = createVectorFilter(type, `filter${k}`, options.params);
|
|
4354
|
+
const at = options.index === void 0 ? list.length : Math.min(list.length, Math.max(0, Math.round(options.index)));
|
|
4355
|
+
next.set(node.id, { ...node, filters: [...list.slice(0, at), filter, ...list.slice(at)] });
|
|
4356
|
+
created.push(filter.id);
|
|
4357
|
+
}
|
|
4358
|
+
this.change(`Add ${VECTOR_FILTER_LABELS[type]}`, () => this.setDoc(mapTree(this.doc.nodes, (n) => next.get(n.id) ?? n)));
|
|
4359
|
+
return created;
|
|
4360
|
+
}
|
|
4361
|
+
editFilters(nodeId, label, fn, merge) {
|
|
4362
|
+
const node = this.getNode(nodeId);
|
|
4363
|
+
if (!node) return { ok: false, message: `There is no node "${nodeId}".` };
|
|
4364
|
+
const result = fn(node.filters ?? []);
|
|
4365
|
+
if (typeof result === "string") return { ok: false, message: result };
|
|
4366
|
+
this.change(label, () => this.replaceNode(nodeId, result.length ? { ...node, filters: result } : (({ filters: _f, ...rest }) => rest)(node)), merge);
|
|
4367
|
+
return { ok: true };
|
|
4368
|
+
}
|
|
4369
|
+
/**
|
|
4370
|
+
* Turns a filter on or off and changes its parameters (invalid values are ignored). With
|
|
4371
|
+
* `merge`, consecutive updates of the same filter fold into one undo step.
|
|
4372
|
+
*/
|
|
4373
|
+
updateFilter(nodeId, filterId, patch, options = {}) {
|
|
4374
|
+
const merge = options.merge === true ? `filter:${nodeId}:${filterId}` : options.merge || void 0;
|
|
4375
|
+
const current = this.getNode(nodeId)?.filters?.find((f) => f.id === filterId);
|
|
4376
|
+
return this.editFilters(
|
|
4377
|
+
nodeId,
|
|
4378
|
+
`Edit ${current ? VECTOR_FILTER_LABELS[current.type] : "Filter"}`,
|
|
4379
|
+
(list) => {
|
|
4380
|
+
const i = list.findIndex((f2) => f2.id === filterId);
|
|
4381
|
+
if (i < 0) return `Node "${nodeId}" has no filter "${filterId}".`;
|
|
4382
|
+
const f = list[i];
|
|
4383
|
+
const params = patch.params ? normalizeVectorFilterParams(f.type, patch.params, f.params) : f.params;
|
|
4384
|
+
const next = { ...f, params, ...patch.enabled !== void 0 ? { enabled: patch.enabled } : {} };
|
|
4385
|
+
if (next.enabled !== false) delete next.enabled;
|
|
4386
|
+
return list.map((x, j) => j === i ? next : x);
|
|
4387
|
+
},
|
|
4388
|
+
merge
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4391
|
+
/** Removes a filter from a node's stack. */
|
|
4392
|
+
removeFilter(nodeId, filterId) {
|
|
4393
|
+
return this.editFilters(nodeId, "Remove Filter", (list) => list.some((f) => f.id === filterId) ? list.filter((f) => f.id !== filterId) : `Node "${nodeId}" has no filter "${filterId}".`);
|
|
4394
|
+
}
|
|
4395
|
+
/** Moves a filter to position `index` of its node's stack. */
|
|
4396
|
+
moveFilter(nodeId, filterId, index) {
|
|
4397
|
+
return this.editFilters(nodeId, "Reorder Filters", (list) => {
|
|
4398
|
+
const i = list.findIndex((f2) => f2.id === filterId);
|
|
4399
|
+
if (i < 0) return `Node "${nodeId}" has no filter "${filterId}".`;
|
|
4400
|
+
const out = list.slice();
|
|
4401
|
+
const [f] = out.splice(i, 1);
|
|
4402
|
+
out.splice(Math.min(out.length, Math.max(0, Math.round(index))), 0, f);
|
|
4403
|
+
return out;
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
// -------------------------------------------------------------------------
|
|
4117
4407
|
// Path effects
|
|
4118
4408
|
// -------------------------------------------------------------------------
|
|
4119
4409
|
/** Selected (or given) nodes that can carry effects: the roots of the selection. */
|
|
@@ -7300,12 +7590,16 @@ function scaleFor(handle, b, p, mods = {}) {
|
|
|
7300
7590
|
return { sx, sy, origin };
|
|
7301
7591
|
}
|
|
7302
7592
|
function withStyle(node, style) {
|
|
7303
|
-
const
|
|
7593
|
+
const { fills, strokes, ...shorthand } = style;
|
|
7594
|
+
let next = { ...node, ...shorthand };
|
|
7304
7595
|
if ("strokeWidthProfile" in style) {
|
|
7305
7596
|
const p = style.strokeWidthProfile ? sanitizeWidthProfile(style.strokeWidthProfile) : [];
|
|
7306
7597
|
if (p.length) next.strokeWidthProfile = p;
|
|
7307
7598
|
else delete next.strokeWidthProfile;
|
|
7308
7599
|
}
|
|
7600
|
+
next = normalizeVectorPaintStacks(next, "shorthand");
|
|
7601
|
+
if (fills !== void 0) next = withVectorFills(next, sanitizeVectorFills(fills ?? []));
|
|
7602
|
+
if (strokes !== void 0) next = withVectorStrokes(next, sanitizeVectorStrokes(strokes ?? []));
|
|
7309
7603
|
return next;
|
|
7310
7604
|
}
|
|
7311
7605
|
function hiddenOrLocked(nodes, id) {
|
|
@@ -7329,37 +7623,99 @@ function pencilPressureProfile(points, pressures) {
|
|
|
7329
7623
|
var fmt2 = (n) => Math.round(n * 100) / 100;
|
|
7330
7624
|
var fmtMatrix = (m) => `matrix(${m.map((n) => Math.round(n * 1e6) / 1e6).join(" ")})`;
|
|
7331
7625
|
var VECTOR_BUILDER_HATCH_GAP = 6;
|
|
7332
|
-
function
|
|
7626
|
+
function nodeFinish(node, filters, idPrefix) {
|
|
7627
|
+
const out = {};
|
|
7628
|
+
const blend = vectorBlendModeCss(node.blendMode);
|
|
7629
|
+
if (blend) out.blendMode = blend;
|
|
7630
|
+
const filter = vectorNodeFilterSvg(node, `${idPrefix}filter-${node.id}`);
|
|
7631
|
+
if (filter) {
|
|
7632
|
+
filters.push(filter);
|
|
7633
|
+
out.filter = `url(#${filter.id})`;
|
|
7634
|
+
}
|
|
7635
|
+
return out;
|
|
7636
|
+
}
|
|
7637
|
+
function lineStyle(node) {
|
|
7638
|
+
return {
|
|
7639
|
+
strokeWidth: node.strokeWidth ?? 1,
|
|
7640
|
+
...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
|
|
7641
|
+
...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
|
|
7642
|
+
...node.strokeDasharray?.length ? { strokeDasharray: node.strokeDasharray.join(" ") } : {}
|
|
7643
|
+
};
|
|
7644
|
+
}
|
|
7645
|
+
function renderLayers(node, defs, idPrefix) {
|
|
7646
|
+
const out = [];
|
|
7647
|
+
for (const l of vectorPaintLayerNodes(node)) {
|
|
7648
|
+
if (!l.drawn) continue;
|
|
7649
|
+
const role = l.kind;
|
|
7650
|
+
const paint = vectorPaintToSvg(l.layer.paint, `${idPrefix}${role}-${node.id}-${l.index}`, { node: l.node, ...role === "stroke" ? { role } : {} });
|
|
7651
|
+
defs.push(...paint.defs);
|
|
7652
|
+
const blend = vectorBlendModeCss(l.layer.blendMode);
|
|
7653
|
+
const base = { kind: role, index: l.index, opacity: l.layer.opacity ?? 1, ...blend ? { blendMode: blend } : {} };
|
|
7654
|
+
if (role === "fill") out.push({ ...base, fill: paint.value, stroke: "none", strokeWidth: 0 });
|
|
7655
|
+
else {
|
|
7656
|
+
const strokeParts = vectorStrokeSvgParts(l.node, `${idPrefix}clip-${node.id}-${l.index}`);
|
|
7657
|
+
out.push({ ...base, fill: "none", stroke: paint.value, ...lineStyle(l.node), ...strokeParts ? { strokeParts } : {} });
|
|
7658
|
+
}
|
|
7659
|
+
}
|
|
7660
|
+
return out;
|
|
7661
|
+
}
|
|
7662
|
+
function renderNode(stored, defs, filters, idPrefix) {
|
|
7333
7663
|
if (stored.visible === false) return null;
|
|
7334
7664
|
const node = expandVectorAppearance(stored);
|
|
7335
7665
|
if (node.type === "group") {
|
|
7336
|
-
const children = node.children.map((c) => renderNode(c, defs, idPrefix)).filter((c) => !!c);
|
|
7337
|
-
return {
|
|
7666
|
+
const children = node.children.map((c) => renderNode(c, defs, filters, idPrefix)).filter((c) => !!c);
|
|
7667
|
+
return {
|
|
7668
|
+
kind: "group",
|
|
7669
|
+
id: node.id,
|
|
7670
|
+
...node.transform ? { transform: fmtMatrix(node.transform) } : {},
|
|
7671
|
+
opacity: node.opacity ?? 1,
|
|
7672
|
+
...nodeFinish(node, filters, idPrefix),
|
|
7673
|
+
children
|
|
7674
|
+
};
|
|
7675
|
+
}
|
|
7676
|
+
const finish = nodeFinish(node, filters, idPrefix);
|
|
7677
|
+
const common = {
|
|
7678
|
+
kind: "path",
|
|
7679
|
+
id: node.id,
|
|
7680
|
+
...node.transform ? { transform: fmtMatrix(node.transform) } : {},
|
|
7681
|
+
d: vectorPathData(node),
|
|
7682
|
+
fillRule: node.fillRule ?? "nonzero",
|
|
7683
|
+
opacity: node.opacity ?? 1,
|
|
7684
|
+
...finish
|
|
7685
|
+
};
|
|
7686
|
+
if (needsVectorPaintLayers(node)) {
|
|
7687
|
+
const layers = renderLayers(node, defs, idPrefix);
|
|
7688
|
+
const fill2 = layers.filter((l) => l.kind === "fill").at(-1);
|
|
7689
|
+
const stroke2 = layers.filter((l) => l.kind === "stroke").at(-1);
|
|
7690
|
+
return {
|
|
7691
|
+
...common,
|
|
7692
|
+
fill: fill2?.fill ?? "none",
|
|
7693
|
+
stroke: stroke2?.stroke ?? "none",
|
|
7694
|
+
strokeWidth: stroke2?.strokeWidth ?? node.strokeWidth ?? 1,
|
|
7695
|
+
...stroke2?.strokeLinecap ? { strokeLinecap: stroke2.strokeLinecap } : {},
|
|
7696
|
+
...stroke2?.strokeLinejoin ? { strokeLinejoin: stroke2.strokeLinejoin } : {},
|
|
7697
|
+
...stroke2?.strokeDasharray ? { strokeDasharray: stroke2.strokeDasharray } : {},
|
|
7698
|
+
...stroke2?.strokeParts ? { strokeParts: stroke2.strokeParts } : {},
|
|
7699
|
+
layers
|
|
7700
|
+
};
|
|
7338
7701
|
}
|
|
7339
7702
|
const fill = vectorPaintToSvg(node.fill, `${idPrefix}fill-${node.id}`, { node });
|
|
7340
7703
|
const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`, { node, role: "stroke" });
|
|
7341
7704
|
defs.push(...fill.defs, ...stroke.defs);
|
|
7342
7705
|
const strokeParts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${idPrefix}clip-${node.id}`) : void 0;
|
|
7343
7706
|
return {
|
|
7344
|
-
|
|
7345
|
-
id: node.id,
|
|
7346
|
-
...node.transform ? { transform: fmtMatrix(node.transform) } : {},
|
|
7707
|
+
...common,
|
|
7347
7708
|
...strokeParts ? { strokeParts } : {},
|
|
7348
|
-
d: vectorPathData(node),
|
|
7349
7709
|
fill: fill.value,
|
|
7350
7710
|
stroke: stroke.value,
|
|
7351
|
-
|
|
7352
|
-
...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
|
|
7353
|
-
...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
|
|
7354
|
-
...node.strokeDasharray?.length ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
|
|
7355
|
-
fillRule: node.fillRule ?? "nonzero",
|
|
7356
|
-
opacity: node.opacity ?? 1
|
|
7711
|
+
...lineStyle(node)
|
|
7357
7712
|
};
|
|
7358
7713
|
}
|
|
7359
7714
|
function getVectorSceneRenderData(state, options = {}) {
|
|
7360
7715
|
const defs = [];
|
|
7716
|
+
const filters = [];
|
|
7361
7717
|
const prefix = options.idPrefix ?? "ve-";
|
|
7362
|
-
const nodes = state.document.nodes.map((n) => renderNode(n, defs, prefix)).filter((n) => !!n);
|
|
7718
|
+
const nodes = state.document.nodes.map((n) => renderNode(n, defs, filters, prefix)).filter((n) => !!n);
|
|
7363
7719
|
const v = state.viewport;
|
|
7364
7720
|
return {
|
|
7365
7721
|
transform: `translate(${fmt2(v.x)} ${fmt2(v.y)}) scale(${v.zoom})`,
|
|
@@ -7367,6 +7723,7 @@ function getVectorSceneRenderData(state, options = {}) {
|
|
|
7367
7723
|
height: state.document.height,
|
|
7368
7724
|
...state.document.background ? { background: state.document.background } : {},
|
|
7369
7725
|
defs,
|
|
7726
|
+
filters,
|
|
7370
7727
|
nodes
|
|
7371
7728
|
};
|
|
7372
7729
|
}
|
|
@@ -7593,7 +7950,7 @@ function getVectorEnvelopePanel(state) {
|
|
|
7593
7950
|
};
|
|
7594
7951
|
}
|
|
7595
7952
|
function gradientOverlay(state) {
|
|
7596
|
-
const a = getVectorGradientAnnotator(state.document, state.selection, state.viewport, state.gradientEdit.target);
|
|
7953
|
+
const a = getVectorGradientAnnotator(state.document, state.selection, state.viewport, state.gradientEdit.target, state.gradientEdit.layer ?? null);
|
|
7597
7954
|
if (!a) return null;
|
|
7598
7955
|
const hover = state.interaction ? null : state.hover?.gradient;
|
|
7599
7956
|
const handleHovered = (h) => hover?.kind === "handle" && hover.handle === h;
|
|
@@ -7831,8 +8188,8 @@ function getVectorGradientEditor(state) {
|
|
|
7831
8188
|
if (node) paths.push(...collect(node));
|
|
7832
8189
|
}
|
|
7833
8190
|
if (!paths.length) return null;
|
|
7834
|
-
const target = state.gradientEdit
|
|
7835
|
-
const paint = paths[0]
|
|
8191
|
+
const { target, layer } = state.gradientEdit;
|
|
8192
|
+
const paint = vectorTargetPaint(paths[0], target, layer ?? null);
|
|
7836
8193
|
const gradient = isVectorGradient(paint) ? paint : null;
|
|
7837
8194
|
const sorted = gradient ? sortedVectorGradientStops(gradient) : [];
|
|
7838
8195
|
const stopIndex = state.gradientEdit.stop;
|
|
@@ -7846,6 +8203,7 @@ function getVectorGradientEditor(state) {
|
|
|
7846
8203
|
}).join(", ")})` : null;
|
|
7847
8204
|
return {
|
|
7848
8205
|
target,
|
|
8206
|
+
layer: layer ?? null,
|
|
7849
8207
|
gradient,
|
|
7850
8208
|
color: vectorPaintToHex(paint),
|
|
7851
8209
|
stops,
|
|
@@ -7856,7 +8214,7 @@ function getVectorGradientEditor(state) {
|
|
|
7856
8214
|
interpolation: gradient?.interpolation ?? "srgb",
|
|
7857
8215
|
smoothness: gradient?.smoothness ?? 0,
|
|
7858
8216
|
strokeMode: gradient?.strokeMode ?? "within",
|
|
7859
|
-
mixed: paths.some((p) => JSON.stringify(p
|
|
8217
|
+
mixed: paths.some((p) => JSON.stringify(vectorTargetPaint(p, target, layer ?? null) ?? null) !== JSON.stringify(paint ?? null))
|
|
7860
8218
|
};
|
|
7861
8219
|
}
|
|
7862
8220
|
var VECTOR_PATH_ACTIONS = [
|
|
@@ -7900,6 +8258,66 @@ function getVectorEffectsPanel(state) {
|
|
|
7900
8258
|
expandable: nodes.some(hasVectorAppearance)
|
|
7901
8259
|
};
|
|
7902
8260
|
}
|
|
8261
|
+
function vectorPaintSwatch(paint) {
|
|
8262
|
+
if (typeof paint === "string") return paint;
|
|
8263
|
+
if (isVectorGradient(paint)) {
|
|
8264
|
+
return `linear-gradient(to right, ${expandVectorGradientStops(paint).map((s) => {
|
|
8265
|
+
const c = parseVectorColor(s.color);
|
|
8266
|
+
const a = (c?.[3] ?? 1) * (s.opacity ?? 1);
|
|
8267
|
+
const color = c ? `rgba(${Math.round(c[0] * 255)}, ${Math.round(c[1] * 255)}, ${Math.round(c[2] * 255)}, ${Math.round(a * 1e3) / 1e3})` : s.color;
|
|
8268
|
+
return `${color} ${Math.round(s.offset * 1e4) / 100}%`;
|
|
8269
|
+
}).join(", ")})`;
|
|
8270
|
+
}
|
|
8271
|
+
return "transparent";
|
|
8272
|
+
}
|
|
8273
|
+
function getVectorAppearancePanel(state) {
|
|
8274
|
+
const nodes = state.selection.map((id) => findVectorNode(state.document.nodes, id)).filter((n) => !!n && !n.blendStep);
|
|
8275
|
+
if (!nodes.length) return null;
|
|
8276
|
+
const paths = [];
|
|
8277
|
+
for (const n of nodes) for (const p of collect(n)) if (!p.blendStep && !paths.some((q) => q.id === p.id)) paths.push(p);
|
|
8278
|
+
const first = paths[0];
|
|
8279
|
+
const edit = state.gradientEdit;
|
|
8280
|
+
const layer = (kind, l, index, count) => ({
|
|
8281
|
+
kind,
|
|
8282
|
+
index,
|
|
8283
|
+
paint: l.paint,
|
|
8284
|
+
color: vectorPaintToHex(l.paint),
|
|
8285
|
+
swatch: vectorPaintSwatch(l.paint),
|
|
8286
|
+
gradient: isVectorGradient(l.paint),
|
|
8287
|
+
opacity: l.opacity ?? 1,
|
|
8288
|
+
blendMode: l.blendMode ?? "normal",
|
|
8289
|
+
visible: l.visible !== false,
|
|
8290
|
+
...kind === "stroke" ? { strokeWidth: l.strokeWidth ?? 1, strokeAlign: l.strokeAlign ?? "center" } : {},
|
|
8291
|
+
...(l.kind ?? "paint") !== "paint" ? { unknownKind: String(l.kind) } : {},
|
|
8292
|
+
editing: edit.target === kind && (edit.layer === null ? index === count - 1 : edit.layer === index)
|
|
8293
|
+
});
|
|
8294
|
+
const fills = first ? vectorFills(first) : [];
|
|
8295
|
+
const strokes = first ? vectorStrokes(first) : [];
|
|
8296
|
+
const same = (get) => paths.every((p) => JSON.stringify(get(p)) === JSON.stringify(get(first)));
|
|
8297
|
+
const one = nodes.length === 1 ? nodes[0] : null;
|
|
8298
|
+
return {
|
|
8299
|
+
nodeIds: nodes.map((n) => n.id),
|
|
8300
|
+
pathIds: paths.map((p) => p.id),
|
|
8301
|
+
fills: fills.map((l, i) => layer("fill", l, i, fills.length)),
|
|
8302
|
+
strokes: strokes.map((l, i) => layer("stroke", l, i, strokes.length)),
|
|
8303
|
+
blendMode: nodes[0].blendMode ?? "normal",
|
|
8304
|
+
mixed: {
|
|
8305
|
+
fills: !!first && !same(vectorFills),
|
|
8306
|
+
strokes: !!first && !same(vectorStrokes),
|
|
8307
|
+
blendMode: nodes.some((n) => (n.blendMode ?? "normal") !== (nodes[0].blendMode ?? "normal"))
|
|
8308
|
+
},
|
|
8309
|
+
filterNodeId: one?.id ?? null,
|
|
8310
|
+
filters: (one?.filters ?? []).map((f) => ({
|
|
8311
|
+
id: f.id,
|
|
8312
|
+
type: f.type,
|
|
8313
|
+
label: VECTOR_FILTER_LABELS[f.type] ?? f.type,
|
|
8314
|
+
enabled: f.enabled !== false,
|
|
8315
|
+
params: f.params,
|
|
8316
|
+
specs: VECTOR_FILTER_PARAM_SPECS[f.type] ?? []
|
|
8317
|
+
}))
|
|
8318
|
+
};
|
|
8319
|
+
}
|
|
8320
|
+
var VECTOR_BLEND_MODE_MENU = VECTOR_BLEND_MODES.map((mode) => ({ mode, label: VECTOR_BLEND_MODE_LABELS[mode] }));
|
|
7903
8321
|
|
|
7904
8322
|
// src/vector/commands.ts
|
|
7905
8323
|
var EFFECT_PARAMS_DESCRIPTION = "Effect parameters (missing ones keep their value or default). trim: start, end (percent), offset (degrees), mode simultaneous|individual. roundCorners: radius. puckerBloat: amount (\u2212100\u2026100). twist: angle, center {x, y} (offset from the middle). zigzag: size, ridges, smooth. wiggle: size, detail, smooth, speed, correlation, phase, seed, time (seconds). offset: amount, join miter|round|bevel, miterLimit, copies, copyOffset. merge: mode merge|add|subtract|intersect|exclude. repeater: copies (fractions fade the last), offset, position {x, y}, anchor {x, y} (offset from the middle), rotation, scale {x, y} (percent), startOpacity, endOpacity (percent), composite below|above. envelope: style (a warp preset: arc|arcLower|arcUpper|arch|bulge|shellLower|shellUpper|flag|wave|fish|rise|fisheye|inflate|squeeze|twist, or mesh), bend, horizontal, vertical (percent, \u2212100\u2026100), orientation horizontal|vertical, rows, columns, mesh { us, vs, points } (node space; prefer vector.envelope).";
|
|
@@ -7983,15 +8401,23 @@ var PAINT = {
|
|
|
7983
8401
|
description: 'A CSS colour, `null` for none, or a gradient object: `{ type: "linear" | "radial" | "conic" | "reflected" | "diamond", stops: [{ offset, color, opacity?, midpoint? }] }` plus the shorthand `angle` / `cx, cy, r` or explicit `start`, `end`, `width`, `focal`, `focalRadius`, `units`, and `spread`, `interpolation`, `smoothness`, `strokeMode` (see vector.setGradient).',
|
|
7984
8402
|
anyOf: [{ type: "string" }, { type: "null" }, { type: "object", properties: GRADIENT_PROPS, required: ["type", "stops"] }]
|
|
7985
8403
|
};
|
|
7986
|
-
var
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
8404
|
+
var BLEND_MODE = {
|
|
8405
|
+
type: "string",
|
|
8406
|
+
enum: VECTOR_BLEND_MODES,
|
|
8407
|
+
description: "How it mixes with what is under it (CSS mix-blend-mode names; linear-dodge draws as plus-lighter). Default normal."
|
|
8408
|
+
};
|
|
8409
|
+
var FILL_LAYER_PROPS = {
|
|
8410
|
+
paint: PAINT,
|
|
8411
|
+
opacity: { type: "number", minimum: 0, maximum: 1, description: "Layer opacity 0 to 1 (default 1), multiplying the paint's own alpha." },
|
|
8412
|
+
blendMode: BLEND_MODE,
|
|
8413
|
+
visible: { type: "boolean", description: "false hides the layer (kept, not drawn or hit). Default true." }
|
|
8414
|
+
};
|
|
8415
|
+
var STROKE_SETTING_PROPS = {
|
|
8416
|
+
strokeWidth: { type: "number", minimum: 0, description: "Stroke width in document units (default 1)." },
|
|
8417
|
+
strokeLinecap: { type: "string", enum: ["butt", "round", "square"], description: "Line cap (default butt)." },
|
|
8418
|
+
strokeLinejoin: { type: "string", enum: ["miter", "round", "bevel"], description: "Line join (default miter)." },
|
|
8419
|
+
strokeDasharray: { type: ["array", "null"], items: { type: "number", minimum: 0 }, description: "Dash and gap lengths in document units; null or [] for a solid line." },
|
|
8420
|
+
strokeAlign: { type: "string", enum: ["center", "inside", "outside"], description: "Where the stroke sits on the outline (closed shapes only; open paths stroke centred). Default center." },
|
|
7995
8421
|
startArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the start of an open path, or null for none." },
|
|
7996
8422
|
endArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the end of an open path, or null for none." },
|
|
7997
8423
|
arrowAlign: { type: "string", enum: ["extend", "tip"], description: "`extend`: the tip sits past the end point; `tip`: on it, the stroke shortened under the head." },
|
|
@@ -7999,12 +8425,33 @@ var STYLE_PROPS = {
|
|
|
7999
8425
|
endArrowScale: { type: "number", exclusiveMinimum: 0, description: "End head size as a multiple of the stroke width (default 1)." },
|
|
8000
8426
|
strokeWidthProfile: WIDTH_POINTS
|
|
8001
8427
|
};
|
|
8428
|
+
var FILL_LAYER = { type: "object", properties: FILL_LAYER_PROPS, required: ["paint"], additionalProperties: false };
|
|
8429
|
+
var STROKE_LAYER = { type: "object", properties: { ...FILL_LAYER_PROPS, ...STROKE_SETTING_PROPS }, required: ["paint"], additionalProperties: false };
|
|
8430
|
+
var STYLE_PROPS = {
|
|
8431
|
+
fills: { type: "array", items: FILL_LAYER, description: "Replace the fill stack, bottom to top (fills draw under strokes). `fill` alone edits the top fill." },
|
|
8432
|
+
strokes: { type: "array", items: STROKE_LAYER, description: "Replace the stroke stack, bottom to top, each stroke with its own settings. `stroke` and the stroke settings alone edit the top stroke." },
|
|
8433
|
+
fill: PAINT,
|
|
8434
|
+
stroke: PAINT,
|
|
8435
|
+
...STROKE_SETTING_PROPS,
|
|
8436
|
+
fillRule: { type: "string", enum: ["nonzero", "evenodd"], description: "Which areas count as inside (default nonzero)." }
|
|
8437
|
+
};
|
|
8438
|
+
var checkPaint = (paint, where) => {
|
|
8439
|
+
if (paint && typeof paint === "object") return sanitizeVectorGradient(paint) ?? fail(`${where} is not a valid gradient: give a type and at least one stop { offset, color }.`);
|
|
8440
|
+
return paint;
|
|
8441
|
+
};
|
|
8002
8442
|
var pickStyle = (params) => {
|
|
8003
8443
|
const out = {};
|
|
8004
8444
|
for (const key of Object.keys(STYLE_PROPS)) if (params[key] !== void 0) out[key] = params[key];
|
|
8005
|
-
for (const key of ["fill", "stroke"])
|
|
8006
|
-
|
|
8007
|
-
|
|
8445
|
+
for (const key of ["fill", "stroke"]) if (out[key] !== void 0) out[key] = checkPaint(out[key], key);
|
|
8446
|
+
for (const key of ["fills", "strokes"]) {
|
|
8447
|
+
const stack = out[key];
|
|
8448
|
+
if (!Array.isArray(stack)) continue;
|
|
8449
|
+
stack.forEach((l, i) => {
|
|
8450
|
+
if (!l || typeof l !== "object") fail(`${key}[${i}] must be a layer object { paint, \u2026 }.`);
|
|
8451
|
+
const layer = l;
|
|
8452
|
+
checkPaint(layer.paint, `${key}[${i}].paint`);
|
|
8453
|
+
if (layer.blendMode !== void 0 && !isVectorBlendMode(layer.blendMode)) fail(`${key}[${i}].blendMode "${String(layer.blendMode)}" is not one of: ${VECTOR_BLEND_MODES.join(", ")}.`);
|
|
8454
|
+
});
|
|
8008
8455
|
}
|
|
8009
8456
|
return out;
|
|
8010
8457
|
};
|
|
@@ -8053,7 +8500,11 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8053
8500
|
...n.blendStep ? { blendStep: true } : {},
|
|
8054
8501
|
...n.effects?.length ? { effects: n.effects } : {},
|
|
8055
8502
|
...n.type === "group" && n.liveBoolean ? { liveBoolean: n.liveBoolean } : {},
|
|
8056
|
-
...n.booleanOperation ? { booleanOperation: n.booleanOperation } : {}
|
|
8503
|
+
...n.booleanOperation ? { booleanOperation: n.booleanOperation } : {},
|
|
8504
|
+
...n.type === "path" && Array.isArray(n.fills) ? { fills: n.fills } : {},
|
|
8505
|
+
...n.type === "path" && Array.isArray(n.strokes) ? { strokes: n.strokes } : {},
|
|
8506
|
+
...n.blendMode && n.blendMode !== "normal" ? { blendMode: n.blendMode } : {},
|
|
8507
|
+
...n.filters?.length ? { filters: n.filters } : {}
|
|
8057
8508
|
});
|
|
8058
8509
|
if (n.type === "group") visit(n.children, depth + 1);
|
|
8059
8510
|
}
|
|
@@ -8061,6 +8512,276 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8061
8512
|
visit(state.document.nodes, 0);
|
|
8062
8513
|
return { width: state.document.width, height: state.document.height, nodes: out, selection: state.selection, tool: state.tool };
|
|
8063
8514
|
};
|
|
8515
|
+
const appearanceOf = (id) => {
|
|
8516
|
+
const n = controller.getNode(id) ?? fail(`There is no node "${id}".`);
|
|
8517
|
+
return {
|
|
8518
|
+
id: n.id,
|
|
8519
|
+
type: n.type,
|
|
8520
|
+
...n.type === "path" ? { fills: vectorFills(n), strokes: vectorStrokes(n), fillRule: n.fillRule ?? "nonzero" } : {},
|
|
8521
|
+
blendMode: n.blendMode ?? "normal",
|
|
8522
|
+
opacity: n.opacity ?? 1,
|
|
8523
|
+
filters: n.filters ?? []
|
|
8524
|
+
};
|
|
8525
|
+
};
|
|
8526
|
+
const pathOf = (id) => {
|
|
8527
|
+
const n = controller.getNode(id) ?? fail(`There is no node "${id}". Call vector.list for ids.`);
|
|
8528
|
+
if (n.type !== "path") fail(`"${id}" is a group; fills and strokes belong to paths (pass a path id, or use ids on vector.addFill / vector.addStroke for every path inside).`);
|
|
8529
|
+
return n;
|
|
8530
|
+
};
|
|
8531
|
+
const layerIndex = (id, kind, index, key = "index") => {
|
|
8532
|
+
const stack = kind === "fill" ? vectorFills(pathOf(id)) : vectorStrokes(pathOf(id));
|
|
8533
|
+
if (!Number.isInteger(index) || !stack[index]) fail(`${key} ${index} is out of range: "${id}" has ${stack.length} ${kind}${stack.length === 1 ? "" : "s"} (0 is the bottom). Read them with vector.getAppearance.`);
|
|
8534
|
+
};
|
|
8535
|
+
const stackResult = (id, kind) => kind === "fill" ? { id, fills: vectorFills(pathOf(id)) } : { id, strokes: vectorStrokes(pathOf(id)) };
|
|
8536
|
+
const layerPatchOf = (params, keys) => {
|
|
8537
|
+
const out = {};
|
|
8538
|
+
for (const key of keys) if (params[key] !== void 0) out[key] = key === "paint" ? checkPaint(params[key], "paint") : params[key];
|
|
8539
|
+
return out;
|
|
8540
|
+
};
|
|
8541
|
+
const pathsUnder = (list2) => {
|
|
8542
|
+
const out = [];
|
|
8543
|
+
const walk = (n) => n.type === "path" ? out.push(n.id) : n.children.forEach(walk);
|
|
8544
|
+
for (const id of list2) walk(controller.getNode(id));
|
|
8545
|
+
return out;
|
|
8546
|
+
};
|
|
8547
|
+
const FILL_KEYS = Object.keys(FILL_LAYER_PROPS);
|
|
8548
|
+
const STROKE_KEYS2 = [...FILL_KEYS, ...Object.keys(STROKE_SETTING_PROPS)];
|
|
8549
|
+
const layerCommands = (kind) => {
|
|
8550
|
+
const Kind = kind === "fill" ? "Fill" : "Stroke";
|
|
8551
|
+
const props = kind === "fill" ? FILL_LAYER_PROPS : { ...FILL_LAYER_PROPS, ...STROKE_SETTING_PROPS };
|
|
8552
|
+
const keys = kind === "fill" ? FILL_KEYS : STROKE_KEYS2;
|
|
8553
|
+
const NODE = { type: "string", description: "Id of the path (from vector.list)." };
|
|
8554
|
+
const INDEX = { type: "integer", minimum: 0, description: `Position of the ${kind} in the path's ${kind} stack, 0 at the bottom (from vector.getAppearance).` };
|
|
8555
|
+
const listKey = `${kind}s`;
|
|
8556
|
+
return [
|
|
8557
|
+
{
|
|
8558
|
+
...base,
|
|
8559
|
+
id: `${prefix}add${Kind}`,
|
|
8560
|
+
label: `Add ${Kind}`,
|
|
8561
|
+
keywords: ["appearance", "layer", kind, "stack", "multiple", "paint"],
|
|
8562
|
+
description: kind === "fill" ? "Adds a fill layer to paths (groups: every path inside), on top of the fill stack or at `index`. Fills draw bottom to top, all under the strokes. Returns each path's new fill stack." : "Adds a stroke layer to paths (groups: every path inside), on top of the stroke stack or at `index`, with its own width, alignment, dashes, caps, joins, arrowheads and profile (unset settings come from the stack's current top stroke). Strokes draw bottom to top, over every fill. Returns each path's new stroke stack.",
|
|
8563
|
+
params: {
|
|
8564
|
+
type: "object",
|
|
8565
|
+
properties: {
|
|
8566
|
+
ids: { ...IDS, description: "Paths or groups to add it to (default: the selection; groups give every path inside)." },
|
|
8567
|
+
...props,
|
|
8568
|
+
paint: { ...PAINT, description: `${PAINT.description} Default ${kind === "fill" ? "#808080" : "#000000"}.` },
|
|
8569
|
+
index: { type: "integer", minimum: 0, description: `Where in the stack to put it, 0 at the bottom (default: on top).` }
|
|
8570
|
+
},
|
|
8571
|
+
additionalProperties: false
|
|
8572
|
+
},
|
|
8573
|
+
undoable: true,
|
|
8574
|
+
enabled: enabledFor,
|
|
8575
|
+
run: (_ctx, params) => {
|
|
8576
|
+
const targets = ids(params);
|
|
8577
|
+
check(controller.addPaintLayer(kind, layerPatchOf(params ?? {}, keys), { ids: targets, index: params?.index }));
|
|
8578
|
+
return { nodes: pathsUnder(targets).map((id) => stackResult(id, kind)) };
|
|
8579
|
+
}
|
|
8580
|
+
},
|
|
8581
|
+
{
|
|
8582
|
+
...base,
|
|
8583
|
+
id: `${prefix}update${Kind}`,
|
|
8584
|
+
label: `Edit ${Kind}`,
|
|
8585
|
+
keywords: ["appearance", "layer", kind, "opacity", "blend", "hide", "show"],
|
|
8586
|
+
description: `Changes ${kind} \`index\` of path \`nodeId\`: ${kind === "fill" ? "paint, opacity, blend mode, visibility" : "paint, opacity, blend mode, visibility and any stroke setting (null removes an arrowhead, dashes or a profile)"}. Missing fields keep their value. Returns the path's ${kind} stack.`,
|
|
8587
|
+
params: { type: "object", properties: { nodeId: NODE, index: INDEX, ...props }, required: ["nodeId", "index"], additionalProperties: false },
|
|
8588
|
+
undoable: true,
|
|
8589
|
+
run: (_ctx, params) => {
|
|
8590
|
+
layerIndex(params.nodeId, kind, params.index);
|
|
8591
|
+
const patch = layerPatchOf(params, keys);
|
|
8592
|
+
if (!Object.keys(patch).length) fail(`Pass at least one of: ${keys.join(", ")}.`);
|
|
8593
|
+
check(controller.updatePaintLayer(kind, params.index, patch, { ids: [params.nodeId] }));
|
|
8594
|
+
return stackResult(params.nodeId, kind);
|
|
8595
|
+
}
|
|
8596
|
+
},
|
|
8597
|
+
{
|
|
8598
|
+
...base,
|
|
8599
|
+
id: `${prefix}remove${Kind}`,
|
|
8600
|
+
label: `Remove ${Kind}`,
|
|
8601
|
+
keywords: ["appearance", "layer", kind, "delete"],
|
|
8602
|
+
description: `Removes ${kind} \`index\` from path \`nodeId\`. Returns the path's remaining ${kind} stack.`,
|
|
8603
|
+
params: { type: "object", properties: { nodeId: NODE, index: INDEX }, required: ["nodeId", "index"], additionalProperties: false },
|
|
8604
|
+
undoable: true,
|
|
8605
|
+
run: (_ctx, params) => {
|
|
8606
|
+
layerIndex(params.nodeId, kind, params.index);
|
|
8607
|
+
check(controller.removePaintLayer(kind, params.index, { ids: [params.nodeId] }));
|
|
8608
|
+
return stackResult(params.nodeId, kind);
|
|
8609
|
+
}
|
|
8610
|
+
},
|
|
8611
|
+
{
|
|
8612
|
+
...base,
|
|
8613
|
+
id: `${prefix}move${Kind}`,
|
|
8614
|
+
label: `Reorder ${Kind}s`,
|
|
8615
|
+
keywords: ["appearance", "layer", kind, "order", "reorder", "stack"],
|
|
8616
|
+
description: `Moves ${kind} \`index\` of path \`nodeId\` to position \`to\` in its ${kind} stack (0 is the bottom). Returns the reordered ${listKey}.`,
|
|
8617
|
+
params: {
|
|
8618
|
+
type: "object",
|
|
8619
|
+
properties: { nodeId: NODE, index: INDEX, to: { type: "integer", minimum: 0, description: `New position, 0 at the bottom (clamped to the stack).` } },
|
|
8620
|
+
required: ["nodeId", "index", "to"],
|
|
8621
|
+
additionalProperties: false
|
|
8622
|
+
},
|
|
8623
|
+
undoable: true,
|
|
8624
|
+
run: (_ctx, params) => {
|
|
8625
|
+
layerIndex(params.nodeId, kind, params.index);
|
|
8626
|
+
check(controller.movePaintLayer(kind, params.index, params.to, { ids: [params.nodeId] }));
|
|
8627
|
+
return stackResult(params.nodeId, kind);
|
|
8628
|
+
}
|
|
8629
|
+
}
|
|
8630
|
+
];
|
|
8631
|
+
};
|
|
8632
|
+
const FILTER_PARAMS = {
|
|
8633
|
+
type: "object",
|
|
8634
|
+
description: "Filter parameters (missing ones keep their value or default; lengths in document units). dropShadow / innerShadow: color (CSS), opacity (0 to 1), offsetX, offsetY, blur (radius; reaches about 1.5 times it), spread (inner: choke). outerGlow / innerGlow: color, opacity, blur, spread. layerBlur: radius. feather: radius (edge fade width).",
|
|
8635
|
+
properties: {
|
|
8636
|
+
color: { type: "string", description: "Shadow or glow colour (CSS)." },
|
|
8637
|
+
opacity: { type: "number", minimum: 0, maximum: 1, description: "Shadow or glow opacity, 0 to 1." },
|
|
8638
|
+
offsetX: { type: "number", description: "Shadow offset to the right, document units." },
|
|
8639
|
+
offsetY: { type: "number", description: "Shadow offset downwards, document units." },
|
|
8640
|
+
blur: { type: "number", minimum: 0, description: "Shadow or glow blur radius, document units." },
|
|
8641
|
+
spread: { type: "number", minimum: 0, description: "Grows the silhouette before blurring (inner shadow and glow: chokes inwards)." },
|
|
8642
|
+
radius: { type: "number", minimum: 0, description: "Layer blur radius, or feather width, document units." }
|
|
8643
|
+
},
|
|
8644
|
+
additionalProperties: false
|
|
8645
|
+
};
|
|
8646
|
+
const checkFilterParams = (type, params) => {
|
|
8647
|
+
const allowed = vectorFilterParamKeys(type);
|
|
8648
|
+
for (const key of Object.keys(params ?? {})) if (!allowed.includes(key)) fail(`${type} has no parameter "${key}"; it takes ${allowed.join(", ")}.`);
|
|
8649
|
+
};
|
|
8650
|
+
const filtersResult = (id) => ({ id, filters: controller.getNode(id)?.filters ?? [] });
|
|
8651
|
+
const filterOf = (nodeId, filterId) => {
|
|
8652
|
+
const node = controller.getNode(nodeId) ?? fail(`There is no node "${nodeId}".`);
|
|
8653
|
+
return node.filters?.find((f) => f.id === filterId) ?? fail(`Node "${nodeId}" has no filter "${filterId}"; it has ${(node.filters ?? []).map((f) => `"${f.id}" (${f.type})`).join(", ") || "none"}.`);
|
|
8654
|
+
};
|
|
8655
|
+
const appearanceCommands = () => [
|
|
8656
|
+
{
|
|
8657
|
+
...base,
|
|
8658
|
+
id: `${prefix}getAppearance`,
|
|
8659
|
+
label: "Get Appearance",
|
|
8660
|
+
readOnly: true,
|
|
8661
|
+
keywords: ["appearance", "fills", "strokes", "blend mode", "filters", "shadow"],
|
|
8662
|
+
description: "Reads a node's appearance: for paths the fill and stroke stacks (bottom to top, each with paint, opacity, blendMode, visible and, for strokes, their settings) and fill rule; for any node its blend mode, opacity and filters (with ids). Use the indexes and ids with the fill, stroke and filter commands.",
|
|
8663
|
+
params: { type: "object", properties: { id: { type: "string", description: "Node id (from vector.list)." } }, required: ["id"], additionalProperties: false },
|
|
8664
|
+
run: (_ctx, params) => appearanceOf(params.id)
|
|
8665
|
+
},
|
|
8666
|
+
...layerCommands("fill"),
|
|
8667
|
+
...layerCommands("stroke"),
|
|
8668
|
+
{
|
|
8669
|
+
...base,
|
|
8670
|
+
id: `${prefix}setBlendMode`,
|
|
8671
|
+
label: "Blend Mode\u2026",
|
|
8672
|
+
keywords: ["blend", "multiply", "screen", "overlay", "mix-blend-mode", "composite"],
|
|
8673
|
+
description: "Sets how whole nodes (paths or groups, with everything they draw) mix with what is under them. `normal` removes it. Returns each node's blend mode.",
|
|
8674
|
+
params: {
|
|
8675
|
+
type: "object",
|
|
8676
|
+
properties: { ids: IDS, blendMode: { ...BLEND_MODE, description: "The blend mode (CSS mix-blend-mode names; linear-dodge draws as plus-lighter)." } },
|
|
8677
|
+
required: ["blendMode"],
|
|
8678
|
+
additionalProperties: false
|
|
8679
|
+
},
|
|
8680
|
+
undoable: true,
|
|
8681
|
+
enabled: enabledFor,
|
|
8682
|
+
run: (_ctx, params) => {
|
|
8683
|
+
const targets = ids(params);
|
|
8684
|
+
check(controller.setBlendMode(params.blendMode, { ids: targets }));
|
|
8685
|
+
return { nodes: targets.map((id) => ({ id, blendMode: controller.getNode(id)?.blendMode ?? "normal" })) };
|
|
8686
|
+
}
|
|
8687
|
+
},
|
|
8688
|
+
{
|
|
8689
|
+
...base,
|
|
8690
|
+
id: `${prefix}addFilter`,
|
|
8691
|
+
label: "Add Filter\u2026",
|
|
8692
|
+
keywords: ["filter", "drop shadow", "inner shadow", "glow", "blur", "feather", "effect", "appearance"],
|
|
8693
|
+
description: "Adds a visual filter `type` (dropShadow, innerShadow, outerGlow, innerGlow, layerBlur, feather) to nodes (paths or groups), at the end of each filter stack or at `index`. Filters never change geometry (bounds and hit-testing ignore them) and export as SVG filters. Returns each node's new filter id and its filter stack.",
|
|
8694
|
+
params: {
|
|
8695
|
+
type: "object",
|
|
8696
|
+
properties: {
|
|
8697
|
+
type: { type: "string", enum: VECTOR_FILTER_TYPES, description: "The filter type." },
|
|
8698
|
+
params: FILTER_PARAMS,
|
|
8699
|
+
index: { type: "integer", minimum: 0, description: "Position in the stack (default: last)." },
|
|
8700
|
+
ids: IDS
|
|
8701
|
+
},
|
|
8702
|
+
required: ["type"],
|
|
8703
|
+
additionalProperties: false
|
|
8704
|
+
},
|
|
8705
|
+
undoable: true,
|
|
8706
|
+
enabled: enabledFor,
|
|
8707
|
+
run: (_ctx, params) => {
|
|
8708
|
+
if (!VECTOR_FILTER_TYPES.includes(params.type)) fail(`Unknown filter type "${params.type}"; use one of ${VECTOR_FILTER_TYPES.join(", ")}.`);
|
|
8709
|
+
checkFilterParams(params.type, params.params);
|
|
8710
|
+
const targets = ids(params);
|
|
8711
|
+
const created = controller.addFilter(params.type, { ids: targets, params: params.params, index: params.index });
|
|
8712
|
+
if (!created.length) fail("Nothing to add the filter to (blend steps take no filters).");
|
|
8713
|
+
return { nodes: targets.map((id, i) => ({ filterId: created[i], ...filtersResult(id) })) };
|
|
8714
|
+
}
|
|
8715
|
+
},
|
|
8716
|
+
{
|
|
8717
|
+
...base,
|
|
8718
|
+
id: `${prefix}updateFilter`,
|
|
8719
|
+
label: "Edit Filter\u2026",
|
|
8720
|
+
keywords: ["filter", "shadow", "glow", "blur", "feather", "enable", "disable"],
|
|
8721
|
+
description: "Changes filter `filterId` of node `nodeId`: its `params` (merged; only the keys its type takes) and whether it is `enabled`. Returns the node's filter stack.",
|
|
8722
|
+
params: {
|
|
8723
|
+
type: "object",
|
|
8724
|
+
properties: {
|
|
8725
|
+
nodeId: { type: "string", description: "Node id." },
|
|
8726
|
+
filterId: { type: "string", description: "Filter id (from vector.addFilter or vector.getAppearance)." },
|
|
8727
|
+
params: FILTER_PARAMS,
|
|
8728
|
+
enabled: { type: "boolean", description: "false turns the filter off (kept in the stack)." }
|
|
8729
|
+
},
|
|
8730
|
+
required: ["nodeId", "filterId"],
|
|
8731
|
+
additionalProperties: false
|
|
8732
|
+
},
|
|
8733
|
+
undoable: true,
|
|
8734
|
+
run: (_ctx, params) => {
|
|
8735
|
+
const filter = filterOf(params.nodeId, params.filterId);
|
|
8736
|
+
checkFilterParams(filter.type, params.params);
|
|
8737
|
+
if (params.params === void 0 && params.enabled === void 0) fail("Pass params or enabled.");
|
|
8738
|
+
check(controller.updateFilter(params.nodeId, params.filterId, { params: params.params, enabled: params.enabled }));
|
|
8739
|
+
return filtersResult(params.nodeId);
|
|
8740
|
+
}
|
|
8741
|
+
},
|
|
8742
|
+
{
|
|
8743
|
+
...base,
|
|
8744
|
+
id: `${prefix}removeFilter`,
|
|
8745
|
+
label: "Remove Filter",
|
|
8746
|
+
keywords: ["filter", "delete", "shadow", "blur"],
|
|
8747
|
+
description: "Removes filter `filterId` from node `nodeId`. Returns the node's remaining filters.",
|
|
8748
|
+
params: {
|
|
8749
|
+
type: "object",
|
|
8750
|
+
properties: { nodeId: { type: "string", description: "Node id." }, filterId: { type: "string", description: "Filter id." } },
|
|
8751
|
+
required: ["nodeId", "filterId"],
|
|
8752
|
+
additionalProperties: false
|
|
8753
|
+
},
|
|
8754
|
+
undoable: true,
|
|
8755
|
+
run: (_ctx, params) => {
|
|
8756
|
+
filterOf(params.nodeId, params.filterId);
|
|
8757
|
+
check(controller.removeFilter(params.nodeId, params.filterId));
|
|
8758
|
+
return filtersResult(params.nodeId);
|
|
8759
|
+
}
|
|
8760
|
+
},
|
|
8761
|
+
{
|
|
8762
|
+
...base,
|
|
8763
|
+
id: `${prefix}moveFilter`,
|
|
8764
|
+
label: "Reorder Filters",
|
|
8765
|
+
keywords: ["filter", "order", "reorder"],
|
|
8766
|
+
description: "Moves filter `filterId` of node `nodeId` to position `index` of its stack. Order matters within a kind: later drop shadows draw over earlier ones, later inner effects over earlier ones. Returns the reordered filters.",
|
|
8767
|
+
params: {
|
|
8768
|
+
type: "object",
|
|
8769
|
+
properties: {
|
|
8770
|
+
nodeId: { type: "string", description: "Node id." },
|
|
8771
|
+
filterId: { type: "string", description: "Filter id." },
|
|
8772
|
+
index: { type: "integer", minimum: 0, description: "New position, 0 first (clamped to the stack)." }
|
|
8773
|
+
},
|
|
8774
|
+
required: ["nodeId", "filterId", "index"],
|
|
8775
|
+
additionalProperties: false
|
|
8776
|
+
},
|
|
8777
|
+
undoable: true,
|
|
8778
|
+
run: (_ctx, params) => {
|
|
8779
|
+
filterOf(params.nodeId, params.filterId);
|
|
8780
|
+
check(controller.moveFilter(params.nodeId, params.filterId, params.index));
|
|
8781
|
+
return filtersResult(params.nodeId);
|
|
8782
|
+
}
|
|
8783
|
+
}
|
|
8784
|
+
];
|
|
8064
8785
|
return [
|
|
8065
8786
|
{
|
|
8066
8787
|
...base,
|
|
@@ -8166,6 +8887,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8166
8887
|
return { ok: true, ...path ? { gradient: path[target ?? "fill"] } : {} };
|
|
8167
8888
|
}
|
|
8168
8889
|
},
|
|
8890
|
+
...appearanceCommands(),
|
|
8169
8891
|
{
|
|
8170
8892
|
...base,
|
|
8171
8893
|
id: `${prefix}setCorners`,
|
|
@@ -8950,6 +9672,9 @@ export {
|
|
|
8950
9672
|
VECTOR_ARROWHEAD_LABELS,
|
|
8951
9673
|
VECTOR_BLEND_MAX_SMOOTH_STEPS,
|
|
8952
9674
|
VECTOR_BLEND_MAX_STEPS,
|
|
9675
|
+
VECTOR_BLEND_MODES,
|
|
9676
|
+
VECTOR_BLEND_MODE_LABELS,
|
|
9677
|
+
VECTOR_BLEND_MODE_MENU,
|
|
8953
9678
|
VECTOR_BLEND_SPACINGS,
|
|
8954
9679
|
VECTOR_BLEND_SPACING_LABELS,
|
|
8955
9680
|
VECTOR_BUILDER_HATCH_GAP,
|
|
@@ -8967,6 +9692,11 @@ export {
|
|
|
8967
9692
|
VECTOR_EFFECT_TYPES,
|
|
8968
9693
|
VECTOR_ENVELOPE_STYLE_OPTIONS,
|
|
8969
9694
|
VECTOR_FILL_GAP,
|
|
9695
|
+
VECTOR_FILTER_DEFAULTS,
|
|
9696
|
+
VECTOR_FILTER_LABELS,
|
|
9697
|
+
VECTOR_FILTER_MENU,
|
|
9698
|
+
VECTOR_FILTER_PARAM_SPECS,
|
|
9699
|
+
VECTOR_FILTER_TYPES,
|
|
8970
9700
|
VECTOR_GRADIENT_INTERPOLATIONS,
|
|
8971
9701
|
VECTOR_GRADIENT_INTERPOLATION_LABELS,
|
|
8972
9702
|
VECTOR_GRADIENT_MAX_PERIODS,
|
|
@@ -8991,6 +9721,7 @@ export {
|
|
|
8991
9721
|
VECTOR_STROKE_ALIGN_LABELS,
|
|
8992
9722
|
VECTOR_STROKE_GRADIENT_MODES,
|
|
8993
9723
|
VECTOR_STROKE_GRADIENT_MODE_LABELS,
|
|
9724
|
+
VECTOR_STROKE_SETTING_KEYS,
|
|
8994
9725
|
VECTOR_STYLE_KEYS,
|
|
8995
9726
|
VECTOR_TOOLS,
|
|
8996
9727
|
VECTOR_TOOL_GLYPHS,
|
|
@@ -9006,6 +9737,7 @@ export {
|
|
|
9006
9737
|
VectorEditorController,
|
|
9007
9738
|
VectorSnapTargets,
|
|
9008
9739
|
activeVectorEffects,
|
|
9740
|
+
activeVectorFilters,
|
|
9009
9741
|
addBuilderHit,
|
|
9010
9742
|
applyMatrix,
|
|
9011
9743
|
applyMatrixLinear,
|
|
@@ -9037,6 +9769,7 @@ export {
|
|
|
9037
9769
|
correspondMorphContours,
|
|
9038
9770
|
createVectorCommands,
|
|
9039
9771
|
createVectorEffect,
|
|
9772
|
+
createVectorFilter,
|
|
9040
9773
|
createVectorMorph,
|
|
9041
9774
|
curvatureMoveAnchor,
|
|
9042
9775
|
cutSubpathAt,
|
|
@@ -9072,6 +9805,7 @@ export {
|
|
|
9072
9805
|
getAnchorWorld,
|
|
9073
9806
|
getEditablePathIds,
|
|
9074
9807
|
getTransformHandlePositions,
|
|
9808
|
+
getVectorAppearancePanel,
|
|
9075
9809
|
getVectorBlendControls,
|
|
9076
9810
|
getVectorCornerWidgets,
|
|
9077
9811
|
getVectorEffectsPanel,
|
|
@@ -9095,8 +9829,11 @@ export {
|
|
|
9095
9829
|
hasStrokeExtras,
|
|
9096
9830
|
hasVectorAppearance,
|
|
9097
9831
|
hasVectorCorners,
|
|
9832
|
+
hasVectorPaintStacks,
|
|
9098
9833
|
hitTestVectorNode,
|
|
9099
9834
|
hitTestVectorNodes,
|
|
9835
|
+
hitTestVectorPaintLayer,
|
|
9836
|
+
hitTestVectorPaintStack,
|
|
9100
9837
|
hitTestVectorStroke,
|
|
9101
9838
|
hitVectorGradientAnnotator,
|
|
9102
9839
|
inferAnchorType,
|
|
@@ -9113,10 +9850,14 @@ export {
|
|
|
9113
9850
|
invertMatrix,
|
|
9114
9851
|
isCentredVectorGradient,
|
|
9115
9852
|
isClosedBezierShape,
|
|
9853
|
+
isDrawnVectorPaintLayer,
|
|
9116
9854
|
isIdentityMatrix,
|
|
9855
|
+
isPlainVectorPaintLayer,
|
|
9117
9856
|
isUniformWidthProfile,
|
|
9118
9857
|
isValidWarpMesh as isValidVectorMesh,
|
|
9119
9858
|
isVectorBlendGroup,
|
|
9859
|
+
isVectorBlendMode,
|
|
9860
|
+
isVectorFilterType,
|
|
9120
9861
|
isVectorGradient,
|
|
9121
9862
|
isVectorLiveBoolean,
|
|
9122
9863
|
isVectorTextTarget,
|
|
@@ -9141,9 +9882,13 @@ export {
|
|
|
9141
9882
|
multiplyMatrices,
|
|
9142
9883
|
nearestSegment,
|
|
9143
9884
|
nearestStrokePosition,
|
|
9885
|
+
needsVectorPaintLayers,
|
|
9886
|
+
newVectorStrokeLayer,
|
|
9144
9887
|
nextVectorCornerKind,
|
|
9145
9888
|
nodeMatrix,
|
|
9146
9889
|
normalizeVectorEffectParams,
|
|
9890
|
+
normalizeVectorFilterParams,
|
|
9891
|
+
normalizeVectorPaintStacks,
|
|
9147
9892
|
offsetPaths,
|
|
9148
9893
|
orderQuad as orderVectorQuad,
|
|
9149
9894
|
parseSvgTransform,
|
|
@@ -9189,7 +9934,11 @@ export {
|
|
|
9189
9934
|
sampleVectorGradientAt,
|
|
9190
9935
|
sampleVectorPaintDefAt,
|
|
9191
9936
|
sanitizeVectorEffects,
|
|
9937
|
+
sanitizeVectorFills,
|
|
9938
|
+
sanitizeVectorFilters,
|
|
9192
9939
|
sanitizeVectorGradient,
|
|
9940
|
+
sanitizeVectorStrokeSettings,
|
|
9941
|
+
sanitizeVectorStrokes,
|
|
9193
9942
|
sanitizeWidthProfile,
|
|
9194
9943
|
scaleFor,
|
|
9195
9944
|
scaleMatrix,
|
|
@@ -9239,6 +9988,8 @@ export {
|
|
|
9239
9988
|
vectorBlendColorDistance,
|
|
9240
9989
|
vectorBlendKeyTargets,
|
|
9241
9990
|
vectorBlendKeys,
|
|
9991
|
+
vectorBlendModeCss,
|
|
9992
|
+
vectorBlendModeFromCss,
|
|
9242
9993
|
vectorBlendRail,
|
|
9243
9994
|
vectorBlendRailAt,
|
|
9244
9995
|
vectorBlendSpine,
|
|
@@ -9259,6 +10010,11 @@ export {
|
|
|
9259
10010
|
vectorEnvelopeMap,
|
|
9260
10011
|
vectorEnvelopeMesh,
|
|
9261
10012
|
vectorFillMap,
|
|
10013
|
+
vectorFills,
|
|
10014
|
+
vectorFilterMarkup,
|
|
10015
|
+
vectorFilterParamKeys,
|
|
10016
|
+
vectorFilterReach,
|
|
10017
|
+
vectorFilterSvg,
|
|
9262
10018
|
vectorGradientArea,
|
|
9263
10019
|
vectorGradientAspect,
|
|
9264
10020
|
vectorGradientBarPosition,
|
|
@@ -9290,9 +10046,13 @@ export {
|
|
|
9290
10046
|
warpMeshRows as vectorMeshRows,
|
|
9291
10047
|
vectorNodeBounds,
|
|
9292
10048
|
vectorNodeEnvelope,
|
|
10049
|
+
vectorNodeFilterSvg,
|
|
10050
|
+
vectorNodeLocalPaintBounds,
|
|
9293
10051
|
vectorNodeRotation,
|
|
9294
10052
|
vectorNodeToBezier,
|
|
9295
10053
|
vectorOperandOperation,
|
|
10054
|
+
vectorPaintLayerNodes,
|
|
10055
|
+
vectorPaintSwatch,
|
|
9296
10056
|
vectorPaintToHex,
|
|
9297
10057
|
vectorPaintToSvg,
|
|
9298
10058
|
vectorParentMatrix,
|
|
@@ -9308,14 +10068,19 @@ export {
|
|
|
9308
10068
|
vectorShapeBuilderMap,
|
|
9309
10069
|
vectorShearForSide,
|
|
9310
10070
|
vectorSkewMatrix,
|
|
10071
|
+
vectorStacksHaveGradient,
|
|
9311
10072
|
vectorStrokeGradientDefs,
|
|
9312
10073
|
vectorStrokeLineArea,
|
|
9313
10074
|
vectorStrokeOutline,
|
|
9314
10075
|
vectorStrokeOutlinePath,
|
|
9315
10076
|
vectorStrokeParts,
|
|
9316
10077
|
vectorStrokeReach,
|
|
10078
|
+
vectorStrokeSettings,
|
|
10079
|
+
vectorStrokeStackReach,
|
|
9317
10080
|
vectorStrokeSvgParts,
|
|
9318
10081
|
vectorStrokeWidthGeometry,
|
|
10082
|
+
vectorStrokes,
|
|
10083
|
+
vectorTargetPaint,
|
|
9319
10084
|
vectorToolLabel,
|
|
9320
10085
|
warpPresetGrid as vectorWarpPresetGrid,
|
|
9321
10086
|
warpPresetMesh as vectorWarpPresetMesh,
|
|
@@ -9328,6 +10093,9 @@ export {
|
|
|
9328
10093
|
widthProfilePresetId,
|
|
9329
10094
|
widthProfileToAttribute,
|
|
9330
10095
|
wigglePaths,
|
|
10096
|
+
withVectorFills,
|
|
10097
|
+
withVectorStrokes,
|
|
10098
|
+
withVectorTargetPaint,
|
|
9331
10099
|
zigzagPaths
|
|
9332
10100
|
};
|
|
9333
10101
|
//# sourceMappingURL=index.js.map
|