@pixerate/editor 0.263.0 → 0.264.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-editor/index.cjs +59 -59
- package/dist/animation-editor/index.js +3 -3
- package/dist/brushes/index.cjs +155 -68
- package/dist/brushes/index.d.cts +3 -3
- package/dist/brushes/index.d.ts +3 -3
- package/dist/brushes/index.js +111 -24
- package/dist/brushes/library.d.cts +2 -0
- package/dist/brushes/library.d.ts +2 -0
- package/dist/brushes/profiles.d.cts +21 -2
- package/dist/brushes/profiles.d.ts +21 -2
- package/dist/brushes/ui.d.cts +57 -4
- package/dist/brushes/ui.d.ts +57 -4
- package/dist/{brushstrokeRender-S5DABANG.js → brushstrokeRender-3MOI7PH4.js} +2 -2
- package/dist/{brushstrokeRender-2GBME4XD.cjs → brushstrokeRender-SMMPIJF2.cjs} +12 -12
- package/dist/canvas/index.cjs +13 -13
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-CNLO6E2K.js → chunk-4OJC4MCD.js} +8 -7
- package/dist/{chunk-ZBACQHW3.cjs → chunk-BY5EBP26.cjs} +27 -6
- package/dist/{chunk-JGI4PZWO.js → chunk-CECCP2V7.js} +1 -1
- package/dist/{chunk-P5343XZO.js → chunk-EE7SAYBF.js} +23 -2
- package/dist/{chunk-TILEUVRO.cjs → chunk-F3KKR6DD.cjs} +35 -34
- package/dist/{chunk-TIHW7CUY.cjs → chunk-FQC5TDB6.cjs} +35 -35
- package/dist/{chunk-4E6BGHWN.cjs → chunk-G5ADO47G.cjs} +1 -1
- package/dist/{chunk-QW7LKWKE.js → chunk-HAUDEVMC.js} +1 -1
- package/dist/{chunk-LG6MEEV7.js → chunk-JEML4FSA.js} +1 -1
- package/dist/{chunk-XQW6R7VW.cjs → chunk-KXNZNOEF.cjs} +86 -86
- package/dist/{chunk-6XX7E7O2.js → chunk-ROBSFJ6F.js} +1 -1
- package/dist/{chunk-LWHUU6A6.cjs → chunk-USJX5SW4.cjs} +10 -10
- package/dist/{chunk-73QFSPDG.cjs → chunk-V7MJ4HIN.cjs} +23 -23
- package/dist/{chunk-34HIZXFX.js → chunk-WWI6FLH2.js} +1 -1
- package/dist/image-editor/controller.d.cts +3 -0
- package/dist/image-editor/controller.d.ts +3 -0
- package/dist/image-editor/index.cjs +789 -786
- package/dist/image-editor/index.js +7 -4
- package/dist/image-editor/types.d.cts +7 -0
- package/dist/image-editor/types.d.ts +7 -0
- package/dist/paint-editor/brush-library-tools.d.cts +8 -1
- package/dist/paint-editor/brush-library-tools.d.ts +8 -1
- package/dist/paint-editor/controller.d.cts +1 -1
- package/dist/paint-editor/controller.d.ts +1 -1
- package/dist/paint-editor/index.cjs +88 -83
- package/dist/paint-editor/index.d.cts +1 -1
- package/dist/paint-editor/index.d.ts +1 -1
- package/dist/paint-editor/index.js +16 -11
- package/dist/vector/controller.d.cts +13 -0
- package/dist/vector/controller.d.ts +13 -0
- package/dist/vector/index.cjs +1215 -1205
- package/dist/vector/index.js +16 -6
- package/package.json +1 -1
- package/skills/pixerate-brushes/SKILL.md +11 -3
- package/skills/pixerate-brushes-design/SKILL.md +1 -0
- package/skills/pixerate-vector/SKILL.md +1 -1
package/dist/brushes/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
createBrushLibraryToolProvider,
|
|
9
9
|
createBrushLibraryTools,
|
|
10
10
|
mergeBrushPatch
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-4OJC4MCD.js";
|
|
12
12
|
import "../chunk-6JL2D5SG.js";
|
|
13
13
|
import {
|
|
14
14
|
BRUSH_FACET_SCHEMAS,
|
|
@@ -44,9 +44,11 @@ import {
|
|
|
44
44
|
brushColourKind,
|
|
45
45
|
brushToJSON,
|
|
46
46
|
checkBrush,
|
|
47
|
+
checkBrushEdit,
|
|
47
48
|
createBrushLibrary,
|
|
48
49
|
degrade,
|
|
49
50
|
getPath,
|
|
51
|
+
isBrushLibrary,
|
|
50
52
|
listPresets,
|
|
51
53
|
migrateBrushV1,
|
|
52
54
|
normalizeBrushSpec,
|
|
@@ -57,8 +59,9 @@ import {
|
|
|
57
59
|
resolveEngine,
|
|
58
60
|
resolveWetMedium,
|
|
59
61
|
setPath,
|
|
60
|
-
snapshotBrushLibrary
|
|
61
|
-
|
|
62
|
+
snapshotBrushLibrary,
|
|
63
|
+
withKeptIssues
|
|
64
|
+
} from "../chunk-EE7SAYBF.js";
|
|
62
65
|
import {
|
|
63
66
|
STAMP_KINDS
|
|
64
67
|
} from "../chunk-ACFSFUQH.js";
|
|
@@ -125,7 +128,11 @@ function brushEditorSections(brush, profile = PAINT_PROFILE) {
|
|
|
125
128
|
const v = getPath(resolved, field.engine);
|
|
126
129
|
return field.map ? field.map.toFacet(v) : v;
|
|
127
130
|
};
|
|
128
|
-
const
|
|
131
|
+
const issues = checkBrush(brush, profile).issues;
|
|
132
|
+
const warned = new Map(issues.filter((i) => i.severity === "warning").map((i) => [i.facet, i.reason]));
|
|
133
|
+
const refused = new Set(issues.filter((i) => i.severity === "error").map((i) => i.facet));
|
|
134
|
+
const full = (s) => s === "full";
|
|
135
|
+
const engineOk = !["kind", "tool", "tip.shape", "marks", "behaviour.surface"].some((f) => refused.has(f));
|
|
129
136
|
const noteFor = (path) => [...warned].find(([facet]) => path === facet || path.startsWith(`${facet}.`))?.[1];
|
|
130
137
|
const withNote = (c) => {
|
|
131
138
|
const note = noteFor(c.path);
|
|
@@ -147,22 +154,23 @@ function brushEditorSections(brush, profile = PAINT_PROFILE) {
|
|
|
147
154
|
const isTool = brush.kind === "tool";
|
|
148
155
|
const shape = brush.tip.shape;
|
|
149
156
|
const marks = brush.marks?.kind ?? "continuous";
|
|
157
|
+
const exchangeFull = full(brush.behaviour?.exchange?.reservoir ? profile.reservoir : profile.exchange);
|
|
150
158
|
add("medium", "Medium", [
|
|
151
|
-
profile.kinds.length > 1 && choice("kind", "Kind", options(profile.kinds, { mark: "Lays a material", tool: "Works on paint" })),
|
|
159
|
+
profile.kinds.length > 1 && profile.kinds.includes(brush.kind) && choice("kind", "Kind", options(profile.kinds, { mark: "Lays a material", tool: "Works on paint" })),
|
|
152
160
|
!isTool && choice("medium", "Medium", [{ value: "", label: "Any" }, ...options(BRUSH_MEDIA, MEDIUM_LABELS)], ""),
|
|
153
161
|
!isTool && withNote({ kind: "text", path: "variety", label: "Variety", ariaLabel: "Brush variety", value: brush.variety ?? "", placeholder: "pencil, oil, highlighter\u2026" }),
|
|
154
|
-
isTool && profile.tools.
|
|
162
|
+
isTool && !!brush.tool && profile.tools.includes(brush.tool) && choice("tool", "Tool", options(profile.tools, TOOL_LABELS))
|
|
155
163
|
]);
|
|
156
164
|
const engineControls = brushEngineControls(resolved);
|
|
157
165
|
const tipSliders = BRUSH_SLIDERS.filter((c) => c.path.startsWith("tip.") && c.path !== "tip.smoothing");
|
|
158
166
|
add("shape", "Shape", [
|
|
159
|
-
choice("tip.shape", "Tip", options(profile.tipShapes.filter((s) => !isTool || s !== "stamp"), SHAPE_LABELS)),
|
|
167
|
+
!refused.has("tip.shape") && choice("tip.shape", "Tip", options(profile.tipShapes.filter((s) => !isTool || s !== "stamp"), SHAPE_LABELS)),
|
|
160
168
|
...tipSliders.map((c) => slider(c)),
|
|
161
169
|
engine === "dab" && choice("tip.blend", "Blend", BLEND_MODES.map((m) => ({ value: m.id, label: m.label })), "normal"),
|
|
162
|
-
...shape === "bristle" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("bristle.")).map((c) => slider(c)) : [],
|
|
163
|
-
shape === "stamp" && withNote({ kind: "stamps", path: "tip.stamps", label: "Stamps", ariaLabel: "Brush stamps", value: [...value("tip.stamps") ?? []], max: marks === "particles" ? MAX_DOT_TEXTURES : MAX_STICKER_STAMPS, options: STAMP_OPTIONS })
|
|
170
|
+
...shape === "bristle" && engineOk && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("bristle.")).map((c) => slider(c)) : [],
|
|
171
|
+
shape === "stamp" && engineOk && withNote({ kind: "stamps", path: "tip.stamps", label: "Stamps", ariaLabel: "Brush stamps", value: [...value("tip.stamps") ?? []], max: marks === "particles" ? MAX_DOT_TEXTURES : MAX_STICKER_STAMPS, options: STAMP_OPTIONS })
|
|
164
172
|
]);
|
|
165
|
-
if (!isTool && profile.marks.length > 1) {
|
|
173
|
+
if (!isTool && profile.marks.length > 1 && engineOk) {
|
|
166
174
|
const particles = marks === "particles" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("particle.") && c.path !== "particle.waveSpeed") : [];
|
|
167
175
|
const objects = marks === "objects" && engineControls ? engineControls.sliders.filter((c) => c.path.startsWith("sticker.")) : [];
|
|
168
176
|
const render = value("marks.render");
|
|
@@ -177,7 +185,7 @@ function brushEditorSections(brush, profile = PAINT_PROFILE) {
|
|
|
177
185
|
...objects.map((c) => slider(c))
|
|
178
186
|
]);
|
|
179
187
|
}
|
|
180
|
-
const feel = BRUSH_SLIDERS.filter((c) => ["tip.smoothing", "pressureSmoothing", "dynamics.minSize", "dynamics.tiltSize", "dynamics.velocitySize"].includes(c.path)).filter((c) => !(c.path === "dynamics.tiltSize" && profile.tilt
|
|
188
|
+
const feel = BRUSH_SLIDERS.filter((c) => ["tip.smoothing", "pressureSmoothing", "dynamics.minSize", "dynamics.tiltSize", "dynamics.velocitySize"].includes(c.path)).filter((c) => !(c.path === "dynamics.tiltSize" && !full(profile.tilt)) && !(c.path === "dynamics.velocitySize" && !full(profile.velocity)));
|
|
181
189
|
add("feel", "Feel", [
|
|
182
190
|
...feel.map((c) => slider(c)),
|
|
183
191
|
toggle("dynamics.pressureSize", "Pressure \u2192 size"),
|
|
@@ -185,19 +193,19 @@ function brushEditorSections(brush, profile = PAINT_PROFILE) {
|
|
|
185
193
|
choice("dynamics.rotation", "Rotation", options(BRUSH_ROTATIONS, ROTATION_LABELS))
|
|
186
194
|
]);
|
|
187
195
|
const surfaces = profile.surfaces;
|
|
188
|
-
const engineSliders = (engineControls?.sliders ?? []).filter((c) => !/^(bristle|particle|sticker)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
|
|
189
|
-
const exchangeOk = (path) => !path?.startsWith("behaviour.exchange") || (path.startsWith("behaviour.exchange.reservoir") ? profile.reservoir
|
|
196
|
+
const engineSliders = (engineOk ? engineControls?.sliders ?? [] : []).filter((c) => !/^(bristle|particle|sticker)\./.test(c.path) && c.path !== "light.amount" && c.path !== "impasto.rim" && c.path !== "impasto.clarity");
|
|
197
|
+
const exchangeOk = (path) => !path?.startsWith("behaviour.exchange") || (path.startsWith("behaviour.exchange.reservoir") ? full(profile.reservoir) : exchangeFull);
|
|
190
198
|
add("behaviour", engineControls?.title ?? "Behaviour", [
|
|
191
|
-
!isTool && surfaces.length > 1 && choice("behaviour.surface", "Surface", options(surfaces, SURFACE_LABELS), "dry"),
|
|
192
|
-
profile.grain
|
|
193
|
-
engine === "wet" && !isTool && choice("behaviour.wet.medium", "Medium", options(WET_MEDIA, WET_MEDIUM_LABELS), "watercolor"),
|
|
194
|
-
engine === "impasto" && choice("behaviour.thick.applicator", "Tool", options(IMPASTO_TOOLS, APPLICATOR_LABELS), "brush"),
|
|
199
|
+
!isTool && surfaces.length > 1 && !refused.has("behaviour.surface") && choice("behaviour.surface", "Surface", options(surfaces, SURFACE_LABELS), "dry"),
|
|
200
|
+
full(profile.grain) && slider({ path: "grain", label: "Grain", min: 0, max: 1, step: 0.01 }, "behaviour.grain"),
|
|
201
|
+
engine === "wet" && engineOk && !isTool && choice("behaviour.wet.medium", "Medium", options(WET_MEDIA, WET_MEDIUM_LABELS), "watercolor"),
|
|
202
|
+
engine === "impasto" && engineOk && choice("behaviour.thick.applicator", "Tool", options(IMPASTO_TOOLS, APPLICATOR_LABELS), "brush"),
|
|
195
203
|
...engineSliders.map((c) => exchangeOk(facetOf(c.path)) ? slider(c) : null),
|
|
196
|
-
engineControls?.mixingPath && fieldOf("behaviour.mixing") && choice("behaviour.mixing", "Mixing", options(["pigment", "rgb"], { pigment: "Pigment", rgb: "RGB" })),
|
|
197
|
-
engineControls?.dirty &&
|
|
198
|
-
engine === "mixer" && toggle("behaviour.exchange.reservoir.refill", "Load each stroke")
|
|
204
|
+
engineOk && engineControls?.mixingPath && (engine === "wet" || exchangeFull) && fieldOf("behaviour.mixing") && choice("behaviour.mixing", "Mixing", options(["pigment", "rgb"], { pigment: "Pigment", rgb: "RGB" })),
|
|
205
|
+
engineOk && engineControls?.dirty && exchangeFull && fieldOf("behaviour.exchange.dirty") && toggle("behaviour.exchange.dirty", "Dirty brush"),
|
|
206
|
+
engine === "mixer" && engineOk && full(profile.reservoir) && toggle("behaviour.exchange.reservoir.refill", "Load each stroke")
|
|
199
207
|
]);
|
|
200
|
-
if (!isTool && profile.light
|
|
208
|
+
if (!isTool && full(profile.light) && engineOk && engineControls?.light) {
|
|
201
209
|
add("light", "Light", [
|
|
202
210
|
slider({ path: "light.amount", label: "Light", min: 0, max: 1, step: 0.01 }),
|
|
203
211
|
toggle("light.pressure", "Light follows pressure"),
|
|
@@ -206,13 +214,13 @@ function brushEditorSections(brush, profile = PAINT_PROFILE) {
|
|
|
206
214
|
toggle("light.glow", "Suggest a glow")
|
|
207
215
|
]);
|
|
208
216
|
}
|
|
209
|
-
if (profile.motion
|
|
217
|
+
if (full(profile.motion) && engineOk && marks === "particles") add("motion", "Motion", [slider({ path: "particle.waveSpeed", label: "Wave speed", min: 0, max: 1, step: 0.01 })]);
|
|
210
218
|
if (!isTool) {
|
|
211
219
|
const own = brush.colour?.default;
|
|
212
220
|
const hex = (c) => typeof c === "string" ? brushColourHex(c, "light") ?? null : null;
|
|
213
221
|
const pair = isDualColor(own) ? own : null;
|
|
214
222
|
add("colour", "Colour", [
|
|
215
|
-
withNote({ kind: "colour", path: "colour.default", label: "Default colour", ariaLabel: "Brush default colour", value: pair ? brushColourHex(pair, "light") ?? null : hex(own), dark: pair ? brushColourHex(pair, "dark") ?? null : null, gradient: own !== void 0 && typeof own === "object" && !pair }),
|
|
223
|
+
!refused.has("colour.default") && withNote({ kind: "colour", path: "colour.default", label: "Default colour", ariaLabel: "Brush default colour", value: pair ? brushColourHex(pair, "light") ?? null : hex(own), dark: pair ? brushColourHex(pair, "dark") ?? null : null, gradient: own !== void 0 && typeof own === "object" && !pair }),
|
|
216
224
|
withNote({
|
|
217
225
|
kind: "palette",
|
|
218
226
|
path: "colour.palette",
|
|
@@ -226,6 +234,78 @@ function brushEditorSections(brush, profile = PAINT_PROFILE) {
|
|
|
226
234
|
}
|
|
227
235
|
return sections;
|
|
228
236
|
}
|
|
237
|
+
var MARKS_WORDS = { particles: "particle marks", objects: "sticker marks", continuous: "continuous marks" };
|
|
238
|
+
function hiddenLabel(issue, b) {
|
|
239
|
+
switch (issue.facet) {
|
|
240
|
+
case "kind":
|
|
241
|
+
case "tool":
|
|
242
|
+
return b.kind === "tool" ? `the ${(TOOL_LABELS[b.tool] ?? b.tool ?? "tool").toLowerCase()} tool` : "its marks";
|
|
243
|
+
case "tip.shape":
|
|
244
|
+
return `a ${b.tip.shape} tip`;
|
|
245
|
+
case "marks":
|
|
246
|
+
return MARKS_WORDS[b.marks?.kind ?? "continuous"] ?? "its marks";
|
|
247
|
+
case "behaviour.surface":
|
|
248
|
+
return `${b.behaviour?.surface ?? "dry"} behaviour`;
|
|
249
|
+
case "behaviour.exchange.reservoir":
|
|
250
|
+
return "a Mixer Brush reservoir";
|
|
251
|
+
case "behaviour.exchange":
|
|
252
|
+
return "paint pick-up";
|
|
253
|
+
case "behaviour.grain":
|
|
254
|
+
return "paper grain";
|
|
255
|
+
case "light":
|
|
256
|
+
return "light";
|
|
257
|
+
case "motion":
|
|
258
|
+
return "motion";
|
|
259
|
+
case "dynamics.velocity":
|
|
260
|
+
return "velocity dynamics";
|
|
261
|
+
case "dynamics.tilt":
|
|
262
|
+
return "tilt dynamics";
|
|
263
|
+
case "colour.default":
|
|
264
|
+
return `a ${brushColourKind(b.colour.default)} default colour`;
|
|
265
|
+
default:
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
var sentence = (words) => words.length < 2 ? words[0] ?? "" : `${words.slice(0, -1).join(", ")} and ${words.at(-1)}`;
|
|
270
|
+
function brushEditorScope(brush, profile = PAINT_PROFILE) {
|
|
271
|
+
const hidden = [];
|
|
272
|
+
for (const issue of checkBrush(brush, profile).issues) {
|
|
273
|
+
const label = hiddenLabel(issue, brush);
|
|
274
|
+
if (label && !hidden.some((h) => h.label === label)) hidden.push({ facet: issue.facet, label });
|
|
275
|
+
}
|
|
276
|
+
return { hidden, note: hidden.length ? `Also has ${sentence(hidden.map((h) => h.label))}, which ${profile.name} can't use.` : null };
|
|
277
|
+
}
|
|
278
|
+
function brushIdFromName(name) {
|
|
279
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "brush";
|
|
280
|
+
}
|
|
281
|
+
function newBrushFor(profile, from, name = "New brush") {
|
|
282
|
+
const id = brushIdFromName(name);
|
|
283
|
+
const base = { id, name, kind: profile.kinds.includes("mark") ? "mark" : profile.kinds[0], ...profile.kinds.includes("mark") ? {} : { tool: profile.tools[0] }, tip: { shape: profile.tipShapes.includes("round") ? "round" : profile.tipShapes[0] } };
|
|
284
|
+
if (from) {
|
|
285
|
+
const stripped = degrade(from, profile, { ignored: true });
|
|
286
|
+
if (!stripped.issues.length) return { ...stripped.brush, id, name };
|
|
287
|
+
}
|
|
288
|
+
return base;
|
|
289
|
+
}
|
|
290
|
+
async function saveBrushEdit(library, options2) {
|
|
291
|
+
const { original, profile } = options2;
|
|
292
|
+
const check = checkBrushEdit(original, options2.brush, profile);
|
|
293
|
+
if (!check.ok) {
|
|
294
|
+
const errors = check.issues.filter((i) => i.severity === "error").map((i) => `${i.facet}: ${i.reason} ${i.fix}`);
|
|
295
|
+
throw new BrushLibraryError("invalid", `${profile.name} can't use this brush: ${errors.join(" ")} Nothing was saved.`);
|
|
296
|
+
}
|
|
297
|
+
const inPlace = !!original && options2.name === void 0 && !library.isBuiltin(original.id) && original.id === options2.brush.id && !!await library.get(original.id);
|
|
298
|
+
let brush = options2.brush;
|
|
299
|
+
if (!inPlace) {
|
|
300
|
+
const name = ((options2.name ?? (original ? `${original.name} copy` : brush.name)).trim() || "Brush").slice(0, 80);
|
|
301
|
+
const stem = brushIdFromName(name).slice(0, 56);
|
|
302
|
+
let id = stem;
|
|
303
|
+
for (let n = 2; library.isBuiltin(id) || await library.get(id); n++) id = `${stem}-${n}`;
|
|
304
|
+
brush = { ...brush, id, name };
|
|
305
|
+
}
|
|
306
|
+
const saved = await library.save(brush, { overwrite: inPlace });
|
|
307
|
+
return { ...saved, kept: check.kept };
|
|
308
|
+
}
|
|
229
309
|
function setBrushEditorValue(brush, path, value) {
|
|
230
310
|
const next = JSON.parse(JSON.stringify(brush));
|
|
231
311
|
if (path === "kind") {
|
|
@@ -418,23 +498,30 @@ export {
|
|
|
418
498
|
brushColourHex,
|
|
419
499
|
brushColourKind,
|
|
420
500
|
brushContracts,
|
|
501
|
+
brushEditorScope,
|
|
421
502
|
brushEditorSections,
|
|
503
|
+
brushIdFromName,
|
|
422
504
|
brushLibraryGroups,
|
|
423
505
|
changedBrushPaths,
|
|
424
506
|
checkBrush,
|
|
507
|
+
checkBrushEdit,
|
|
425
508
|
createBrushLibrary,
|
|
426
509
|
createBrushLibraryToolProvider,
|
|
427
510
|
createBrushLibraryTools,
|
|
428
511
|
degrade,
|
|
512
|
+
isBrushLibrary,
|
|
429
513
|
listPresets,
|
|
430
514
|
mergeBrushPatch,
|
|
431
515
|
migrateBrushV1,
|
|
516
|
+
newBrushFor,
|
|
432
517
|
normalizeBrushSpec,
|
|
433
518
|
parseBrushLibrary,
|
|
434
519
|
resolveBrush,
|
|
435
520
|
resolveCategory,
|
|
436
521
|
resolveEngine,
|
|
437
522
|
resolveWetMedium,
|
|
523
|
+
saveBrushEdit,
|
|
438
524
|
setBrushEditorValue,
|
|
439
|
-
snapshotBrushLibrary
|
|
525
|
+
snapshotBrushLibrary,
|
|
526
|
+
withKeptIssues
|
|
440
527
|
};
|
|
@@ -91,6 +91,8 @@ export interface BrushLibrary {
|
|
|
91
91
|
/** Called after every change. Returns an unsubscribe function. */
|
|
92
92
|
subscribe(listener: () => void): () => void;
|
|
93
93
|
}
|
|
94
|
+
/** Whether `library` is a whole `BrushLibrary` (it can save), not only a list to read: hosts offer brush editing on one. */
|
|
95
|
+
export declare function isBrushLibrary(library: unknown): library is BrushLibrary;
|
|
94
96
|
/**
|
|
95
97
|
* Brushes read from a library file: `{ format: 'pixerate-brushes', version: 1 | 2, brushes }` or
|
|
96
98
|
* a bare array. Version 1 brushes are migrated (`migrateBrushV1`); every brush is normalised.
|
|
@@ -91,6 +91,8 @@ export interface BrushLibrary {
|
|
|
91
91
|
/** Called after every change. Returns an unsubscribe function. */
|
|
92
92
|
subscribe(listener: () => void): () => void;
|
|
93
93
|
}
|
|
94
|
+
/** Whether `library` is a whole `BrushLibrary` (it can save), not only a list to read: hosts offer brush editing on one. */
|
|
95
|
+
export declare function isBrushLibrary(library: unknown): library is BrushLibrary;
|
|
94
96
|
/**
|
|
95
97
|
* Brushes read from a library file: `{ format: 'pixerate-brushes', version: 1 | 2, brushes }` or
|
|
96
98
|
* a bare array. Version 1 brushes are migrated (`migrateBrushV1`); every brush is normalised.
|
|
@@ -66,6 +66,21 @@ export declare const BRUSH_HOST_PROFILES: Readonly<Record<string, BrushHostProfi
|
|
|
66
66
|
export declare function brushColourKind(paint: BrushPaint): BrushColourKind;
|
|
67
67
|
/** What a brush needs from a host, and what the host does about it. */
|
|
68
68
|
export declare function checkBrush(brush: Brush, profile: BrushHostProfile): BrushCheck;
|
|
69
|
+
export interface BrushEditCheck extends BrushCheck {
|
|
70
|
+
/**
|
|
71
|
+
* Errors the brush had before the edit: facets this host can't use, kept unchanged for the
|
|
72
|
+
* hosts that can (a light paint brush edited in Canvas keeps its light). Not in `issues`.
|
|
73
|
+
*/
|
|
74
|
+
kept: BrushIssue[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A check of an edit: errors the original brush already had (`before`, its check for the same
|
|
78
|
+
* host) are kept rather than refused, so a host edits what it renders and leaves the rest alone.
|
|
79
|
+
* Without `before` (a new brush) every error refuses it.
|
|
80
|
+
*/
|
|
81
|
+
export declare function withKeptIssues(check: BrushCheck, before: BrushCheck | null): BrushEditCheck;
|
|
82
|
+
/** `checkBrush` for an edit of `original` (`null`: a new brush), keeping facets it already had that the host can't use. */
|
|
83
|
+
export declare function checkBrushEdit(original: Brush | null, brush: Brush, profile: BrushHostProfile): BrushEditCheck;
|
|
69
84
|
/** The presets (default: the built-in ones) a host can render, in library order. */
|
|
70
85
|
export declare function listPresets(profile: BrushHostProfile, presets?: readonly Brush[]): Brush[];
|
|
71
86
|
export interface DegradedBrush {
|
|
@@ -80,6 +95,10 @@ export interface DegradedBrush {
|
|
|
80
95
|
* The brush the host can render: facets it refuses are removed (light, motion, a reservoir, a
|
|
81
96
|
* wet or thick surface, particle or object marks, bristle and stamp tips become round, an
|
|
82
97
|
* unsupported default colour), each reported in `dropped`. A tool the host lacks can't be
|
|
83
|
-
* stripped and stays in `issues`.
|
|
98
|
+
* stripped and stays in `issues`. `ignored: true` also removes what the host paints without
|
|
99
|
+
* (its warnings: velocity on canvas, grain in the image editor, palette colours it skips), for
|
|
100
|
+
* a brush made only of what the host uses.
|
|
84
101
|
*/
|
|
85
|
-
export declare function degrade(brush: Brush, profile: BrushHostProfile
|
|
102
|
+
export declare function degrade(brush: Brush, profile: BrushHostProfile, options?: {
|
|
103
|
+
ignored?: boolean;
|
|
104
|
+
}): DegradedBrush;
|
|
@@ -66,6 +66,21 @@ export declare const BRUSH_HOST_PROFILES: Readonly<Record<string, BrushHostProfi
|
|
|
66
66
|
export declare function brushColourKind(paint: BrushPaint): BrushColourKind;
|
|
67
67
|
/** What a brush needs from a host, and what the host does about it. */
|
|
68
68
|
export declare function checkBrush(brush: Brush, profile: BrushHostProfile): BrushCheck;
|
|
69
|
+
export interface BrushEditCheck extends BrushCheck {
|
|
70
|
+
/**
|
|
71
|
+
* Errors the brush had before the edit: facets this host can't use, kept unchanged for the
|
|
72
|
+
* hosts that can (a light paint brush edited in Canvas keeps its light). Not in `issues`.
|
|
73
|
+
*/
|
|
74
|
+
kept: BrushIssue[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A check of an edit: errors the original brush already had (`before`, its check for the same
|
|
78
|
+
* host) are kept rather than refused, so a host edits what it renders and leaves the rest alone.
|
|
79
|
+
* Without `before` (a new brush) every error refuses it.
|
|
80
|
+
*/
|
|
81
|
+
export declare function withKeptIssues(check: BrushCheck, before: BrushCheck | null): BrushEditCheck;
|
|
82
|
+
/** `checkBrush` for an edit of `original` (`null`: a new brush), keeping facets it already had that the host can't use. */
|
|
83
|
+
export declare function checkBrushEdit(original: Brush | null, brush: Brush, profile: BrushHostProfile): BrushEditCheck;
|
|
69
84
|
/** The presets (default: the built-in ones) a host can render, in library order. */
|
|
70
85
|
export declare function listPresets(profile: BrushHostProfile, presets?: readonly Brush[]): Brush[];
|
|
71
86
|
export interface DegradedBrush {
|
|
@@ -80,6 +95,10 @@ export interface DegradedBrush {
|
|
|
80
95
|
* The brush the host can render: facets it refuses are removed (light, motion, a reservoir, a
|
|
81
96
|
* wet or thick surface, particle or object marks, bristle and stamp tips become round, an
|
|
82
97
|
* unsupported default colour), each reported in `dropped`. A tool the host lacks can't be
|
|
83
|
-
* stripped and stays in `issues`.
|
|
98
|
+
* stripped and stays in `issues`. `ignored: true` also removes what the host paints without
|
|
99
|
+
* (its warnings: velocity on canvas, grain in the image editor, palette colours it skips), for
|
|
100
|
+
* a brush made only of what the host uses.
|
|
84
101
|
*/
|
|
85
|
-
export declare function degrade(brush: Brush, profile: BrushHostProfile
|
|
102
|
+
export declare function degrade(brush: Brush, profile: BrushHostProfile, options?: {
|
|
103
|
+
ignored?: boolean;
|
|
104
|
+
}): DegradedBrush;
|
package/dist/brushes/ui.d.cts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* Sliders reuse the paint editor's tables (`BRUSH_SLIDERS`, `brushEngineControls`), mapped from
|
|
9
9
|
* engine paths to facet paths through `BRUSH_FIELDS`, so the panels and the editor agree.
|
|
10
10
|
*/
|
|
11
|
-
import type
|
|
12
|
-
import { type BrushHostProfile } from './profiles.cjs';
|
|
11
|
+
import { type BrushLibrary, type BrushLibraryEntry, type SaveBrushResult } from './library.cjs';
|
|
12
|
+
import { type BrushHostProfile, type BrushIssue } from './profiles.cjs';
|
|
13
13
|
import { type Brush } from './types.cjs';
|
|
14
14
|
export type BrushEditorSectionId = 'medium' | 'shape' | 'marks' | 'feel' | 'behaviour' | 'light' | 'motion' | 'colour';
|
|
15
15
|
interface ControlBase {
|
|
@@ -79,10 +79,63 @@ export interface BrushEditorSection {
|
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Sections of controls for a brush, in order, as the host `profile` (default the paint editor)
|
|
82
|
-
* can render them: sections
|
|
83
|
-
*
|
|
82
|
+
* can render them: only the sections, controls and options the host uses are listed. Facets of
|
|
83
|
+
* the brush the host can't use (a paint brush's light opened in Canvas) get no control, so an
|
|
84
|
+
* edit keeps them as they are; `brushEditorScope` names them. Values are the brush's own, else
|
|
85
|
+
* the defaults of the engine it resolves to.
|
|
84
86
|
*/
|
|
85
87
|
export declare function brushEditorSections(brush: Brush, profile?: BrushHostProfile): BrushEditorSection[];
|
|
88
|
+
/** What a brush has that the host can't use: one facet, in words. */
|
|
89
|
+
export interface BrushEditorHiddenFacet {
|
|
90
|
+
facet: string;
|
|
91
|
+
/** In words, for a sentence: `light`, `wet behaviour`, `a bristle tip`. */
|
|
92
|
+
label: string;
|
|
93
|
+
}
|
|
94
|
+
export interface BrushEditorScope {
|
|
95
|
+
/** Facets the brush has that the host can't use (refused or painted without): they get no control and an edit keeps them. */
|
|
96
|
+
hidden: BrushEditorHiddenFacet[];
|
|
97
|
+
/** A short note naming them ("Also has light and wet behaviour, which Canvas can't use."), or `null`. */
|
|
98
|
+
note: string | null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What the editor leaves out of a brush for this host: the facets the host refuses or paints
|
|
102
|
+
* without, which `brushEditorSections` gives no control, so saving an edit keeps them unchanged
|
|
103
|
+
* for the hosts that use them. `note` names them for the editor to show.
|
|
104
|
+
*/
|
|
105
|
+
export declare function brushEditorScope(brush: Brush, profile?: BrushHostProfile): BrushEditorScope;
|
|
106
|
+
/** A brush id from a name: lowercase letters, digits and dashes (`'brush'` when nothing is left). */
|
|
107
|
+
export declare function brushIdFromName(name: string): string;
|
|
108
|
+
/**
|
|
109
|
+
* A new brush for a host to start editing: `from` (the brush in use) with every facet the host
|
|
110
|
+
* can't use removed (`degrade` with `ignored`), else a plain round brush. It passes `checkBrush`
|
|
111
|
+
* for that host, and the editor then offers only what the host uses, so it stays that way.
|
|
112
|
+
*/
|
|
113
|
+
export declare function newBrushFor(profile: BrushHostProfile, from?: Brush | null, name?: string): Brush;
|
|
114
|
+
export interface SaveBrushEditOptions {
|
|
115
|
+
/** The brush as it was before editing (`null`: a new brush). Facets of it the host can't use are kept. */
|
|
116
|
+
original: Brush | null;
|
|
117
|
+
/** The edited brush. */
|
|
118
|
+
brush: Brush;
|
|
119
|
+
/** The host the edit was made in. */
|
|
120
|
+
profile: BrushHostProfile;
|
|
121
|
+
/**
|
|
122
|
+
* Save as a new brush with this name (its id made from the name, `-2`… when taken). Without
|
|
123
|
+
* it the original is changed in place, or a copy is saved when it is built in or new.
|
|
124
|
+
*/
|
|
125
|
+
name?: string;
|
|
126
|
+
}
|
|
127
|
+
/** What `saveBrushEdit` stored, plus the facets kept for other hosts. */
|
|
128
|
+
export interface SaveBrushEditResult extends SaveBrushResult {
|
|
129
|
+
/** Facets this host can't use that the brush keeps (see `checkBrushEdit`). */
|
|
130
|
+
kept: BrushIssue[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Saves a brush edited in a host's editor to the shared library, by the rule the library tools
|
|
134
|
+
* follow too: the edit may not add what the host can't use (a new brush must pass `checkBrush`),
|
|
135
|
+
* and what the original had for other hosts is kept. Built-in presets are never changed: the
|
|
136
|
+
* edit is saved as a copy (`<name> copy`). Refusals are `BrushLibraryError`s with the facet and fix.
|
|
137
|
+
*/
|
|
138
|
+
export declare function saveBrushEdit(library: Pick<BrushLibrary, 'get' | 'save' | 'isBuiltin'>, options: SaveBrushEditOptions): Promise<SaveBrushEditResult>;
|
|
86
139
|
/**
|
|
87
140
|
* The brush with one control's value set (`undefined` removes it), normalised. Changing the kind,
|
|
88
141
|
* the marks or the tip shape starts that facet afresh. Returns the brush and what was clamped or
|
package/dist/brushes/ui.d.ts
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* Sliders reuse the paint editor's tables (`BRUSH_SLIDERS`, `brushEngineControls`), mapped from
|
|
9
9
|
* engine paths to facet paths through `BRUSH_FIELDS`, so the panels and the editor agree.
|
|
10
10
|
*/
|
|
11
|
-
import type
|
|
12
|
-
import { type BrushHostProfile } from './profiles.js';
|
|
11
|
+
import { type BrushLibrary, type BrushLibraryEntry, type SaveBrushResult } from './library.js';
|
|
12
|
+
import { type BrushHostProfile, type BrushIssue } from './profiles.js';
|
|
13
13
|
import { type Brush } from './types.js';
|
|
14
14
|
export type BrushEditorSectionId = 'medium' | 'shape' | 'marks' | 'feel' | 'behaviour' | 'light' | 'motion' | 'colour';
|
|
15
15
|
interface ControlBase {
|
|
@@ -79,10 +79,63 @@ export interface BrushEditorSection {
|
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Sections of controls for a brush, in order, as the host `profile` (default the paint editor)
|
|
82
|
-
* can render them: sections
|
|
83
|
-
*
|
|
82
|
+
* can render them: only the sections, controls and options the host uses are listed. Facets of
|
|
83
|
+
* the brush the host can't use (a paint brush's light opened in Canvas) get no control, so an
|
|
84
|
+
* edit keeps them as they are; `brushEditorScope` names them. Values are the brush's own, else
|
|
85
|
+
* the defaults of the engine it resolves to.
|
|
84
86
|
*/
|
|
85
87
|
export declare function brushEditorSections(brush: Brush, profile?: BrushHostProfile): BrushEditorSection[];
|
|
88
|
+
/** What a brush has that the host can't use: one facet, in words. */
|
|
89
|
+
export interface BrushEditorHiddenFacet {
|
|
90
|
+
facet: string;
|
|
91
|
+
/** In words, for a sentence: `light`, `wet behaviour`, `a bristle tip`. */
|
|
92
|
+
label: string;
|
|
93
|
+
}
|
|
94
|
+
export interface BrushEditorScope {
|
|
95
|
+
/** Facets the brush has that the host can't use (refused or painted without): they get no control and an edit keeps them. */
|
|
96
|
+
hidden: BrushEditorHiddenFacet[];
|
|
97
|
+
/** A short note naming them ("Also has light and wet behaviour, which Canvas can't use."), or `null`. */
|
|
98
|
+
note: string | null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What the editor leaves out of a brush for this host: the facets the host refuses or paints
|
|
102
|
+
* without, which `brushEditorSections` gives no control, so saving an edit keeps them unchanged
|
|
103
|
+
* for the hosts that use them. `note` names them for the editor to show.
|
|
104
|
+
*/
|
|
105
|
+
export declare function brushEditorScope(brush: Brush, profile?: BrushHostProfile): BrushEditorScope;
|
|
106
|
+
/** A brush id from a name: lowercase letters, digits and dashes (`'brush'` when nothing is left). */
|
|
107
|
+
export declare function brushIdFromName(name: string): string;
|
|
108
|
+
/**
|
|
109
|
+
* A new brush for a host to start editing: `from` (the brush in use) with every facet the host
|
|
110
|
+
* can't use removed (`degrade` with `ignored`), else a plain round brush. It passes `checkBrush`
|
|
111
|
+
* for that host, and the editor then offers only what the host uses, so it stays that way.
|
|
112
|
+
*/
|
|
113
|
+
export declare function newBrushFor(profile: BrushHostProfile, from?: Brush | null, name?: string): Brush;
|
|
114
|
+
export interface SaveBrushEditOptions {
|
|
115
|
+
/** The brush as it was before editing (`null`: a new brush). Facets of it the host can't use are kept. */
|
|
116
|
+
original: Brush | null;
|
|
117
|
+
/** The edited brush. */
|
|
118
|
+
brush: Brush;
|
|
119
|
+
/** The host the edit was made in. */
|
|
120
|
+
profile: BrushHostProfile;
|
|
121
|
+
/**
|
|
122
|
+
* Save as a new brush with this name (its id made from the name, `-2`… when taken). Without
|
|
123
|
+
* it the original is changed in place, or a copy is saved when it is built in or new.
|
|
124
|
+
*/
|
|
125
|
+
name?: string;
|
|
126
|
+
}
|
|
127
|
+
/** What `saveBrushEdit` stored, plus the facets kept for other hosts. */
|
|
128
|
+
export interface SaveBrushEditResult extends SaveBrushResult {
|
|
129
|
+
/** Facets this host can't use that the brush keeps (see `checkBrushEdit`). */
|
|
130
|
+
kept: BrushIssue[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Saves a brush edited in a host's editor to the shared library, by the rule the library tools
|
|
134
|
+
* follow too: the edit may not add what the host can't use (a new brush must pass `checkBrush`),
|
|
135
|
+
* and what the original had for other hosts is kept. Built-in presets are never changed: the
|
|
136
|
+
* edit is saved as a copy (`<name> copy`). Refusals are `BrushLibraryError`s with the facet and fix.
|
|
137
|
+
*/
|
|
138
|
+
export declare function saveBrushEdit(library: Pick<BrushLibrary, 'get' | 'save' | 'isBuiltin'>, options: SaveBrushEditOptions): Promise<SaveBrushEditResult>;
|
|
86
139
|
/**
|
|
87
140
|
* The brush with one control's value set (`undefined` removes it), normalised. Changing the kind,
|
|
88
141
|
* the marks or the tip shape starts that facet afresh. Returns the brush and what was clamped or
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
pathDataToPolyline
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JEML4FSA.js";
|
|
4
4
|
import {
|
|
5
5
|
BRUSH_PRESETS,
|
|
6
6
|
BUILTIN_BRUSHES,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
normalizeBrushSpec,
|
|
12
12
|
resolveBrush,
|
|
13
13
|
resolveCategory
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EE7SAYBF.js";
|
|
15
15
|
import "./chunk-ACFSFUQH.js";
|
|
16
16
|
import {
|
|
17
17
|
formatNumber,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkG5ADO47Gcjs = require('./chunk-G5ADO47G.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ var _chunk4E6BGHWNcjs = require('./chunk-4E6BGHWN.cjs');
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var _chunkBY5EBP26cjs = require('./chunk-BY5EBP26.cjs');
|
|
15
15
|
require('./chunk-OTN6VB5S.cjs');
|
|
16
16
|
|
|
17
17
|
|
|
@@ -45,17 +45,17 @@ require('./chunk-5UEFBM2M.cjs');
|
|
|
45
45
|
require('./chunk-IPJ3TEYR.cjs');
|
|
46
46
|
|
|
47
47
|
// src/canvas/brushstrokeRender.ts
|
|
48
|
-
var PRESETS =
|
|
48
|
+
var PRESETS = _chunkBY5EBP26cjs.listPresets.call(void 0, _chunkBY5EBP26cjs.CANVAS_PROFILE).map((spec) => _chunkBY5EBP26cjs.BUILTIN_BRUSHES.find((b) => b.id === spec.id));
|
|
49
49
|
var presetIds = () => PRESETS.map((b) => b.id).join(", ");
|
|
50
|
-
var refusal = (brush) =>
|
|
50
|
+
var refusal = (brush) => _chunkBY5EBP26cjs.checkBrush.call(void 0, brush, _chunkBY5EBP26cjs.CANVAS_PROFILE).issues.filter((i) => i.severity === "error").map((i) => `${i.reason} ${i.fix}`).join(" ");
|
|
51
51
|
function readCanvasBrushSpec(spec) {
|
|
52
52
|
let brush;
|
|
53
53
|
try {
|
|
54
|
-
brush =
|
|
54
|
+
brush = _chunkBY5EBP26cjs.normalizeBrushSpec.call(void 0, spec);
|
|
55
55
|
} catch (e) {
|
|
56
56
|
throw new Error(`Not a brush: ${e.message}`);
|
|
57
57
|
}
|
|
58
|
-
if (!
|
|
58
|
+
if (!_chunkBY5EBP26cjs.checkBrush.call(void 0, brush, _chunkBY5EBP26cjs.CANVAS_PROFILE).ok) throw new Error(`Brush '${brush.id}' (${brush.name}) can't brush a canvas stroke: ${refusal(brush)}`);
|
|
59
59
|
return brush;
|
|
60
60
|
}
|
|
61
61
|
function listCanvasBrushes(user = []) {
|
|
@@ -63,12 +63,12 @@ function listCanvasBrushes(user = []) {
|
|
|
63
63
|
const d = _optionalChain([spec, 'access', _2 => _2.colour, 'optionalAccess', _3 => _3.default]);
|
|
64
64
|
return typeof d === "string" || _chunkMLLTWAXHcjs.isDualColor.call(void 0, d) ? { defaultColor: d } : {};
|
|
65
65
|
};
|
|
66
|
-
const out = PRESETS.map((b) => ({ id: b.id, name: b.name, category: b.category, size: b.tip.size, builtin: true, ...colourOf(
|
|
66
|
+
const out = PRESETS.map((b) => ({ id: b.id, name: b.name, category: b.category, size: b.tip.size, builtin: true, ...colourOf(_chunkBY5EBP26cjs.BRUSH_PRESETS.find((p) => p.id === b.id)) }));
|
|
67
67
|
for (const raw of user) {
|
|
68
68
|
try {
|
|
69
69
|
const brush = readCanvasBrushSpec(raw);
|
|
70
70
|
if (out.some((b) => b.id === brush.id)) continue;
|
|
71
|
-
out.push({ id: brush.id, name: brush.name, category:
|
|
71
|
+
out.push({ id: brush.id, name: brush.name, category: _chunkBY5EBP26cjs.resolveCategory.call(void 0, brush), size: _chunkBY5EBP26cjs.resolveBrush.call(void 0, brush).tip.size, builtin: false, spec: brush, ...colourOf(brush) });
|
|
72
72
|
} catch (e2) {
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -79,15 +79,15 @@ function resolveCanvasBrush(brush, width, spec) {
|
|
|
79
79
|
const sized = (b) => given === void 0 ? b : { ...b, tip: { ...b.tip, size: given } };
|
|
80
80
|
if (typeof brush === "string") {
|
|
81
81
|
if (spec !== void 0 && spec !== null) {
|
|
82
|
-
const saved =
|
|
82
|
+
const saved = _chunkBY5EBP26cjs.resolveBrush.call(void 0, readCanvasBrushSpec(spec));
|
|
83
83
|
if (!_chunkPXDIAYB6cjs.isBrushstrokeBrush.call(void 0, saved)) throw new Error(`Brush '${saved.id}' (${saved.name}) can't brush a canvas stroke: it is not a dry dab brush.`);
|
|
84
84
|
return sized(saved);
|
|
85
85
|
}
|
|
86
86
|
const preset = PRESETS.find((b) => b.id === brush);
|
|
87
87
|
if (preset) return sized(preset);
|
|
88
|
-
const other =
|
|
88
|
+
const other = _chunkBY5EBP26cjs.BRUSH_PRESETS.find((b) => b.id === brush);
|
|
89
89
|
if (other) throw new Error(`Brush '${brush}' (${other.name}) can't brush a canvas stroke: ${refusal(other)} Use one of: ${presetIds()}, or a saved brush the canvas can paint.`);
|
|
90
|
-
throw new Error(`Unknown brush '${brush}'. ${
|
|
90
|
+
throw new Error(`Unknown brush '${brush}'. ${_chunkBY5EBP26cjs.closest.call(void 0, brush, PRESETS.map((b) => b.id))}Use one of the dry presets (${presetIds()}), a saved brush from the brush library, or tip settings ({ hardness, spacing, flow, \u2026 }).`);
|
|
91
91
|
}
|
|
92
92
|
if (!brush || typeof brush !== "object" || Array.isArray(brush)) {
|
|
93
93
|
throw new Error("A brush is a preset or saved brush id (see listCanvasBrushes / canvas.listBrushes) or an object of tip settings.");
|
|
@@ -123,7 +123,7 @@ function renderShapeBrushstrokeNow(shape, scale, place = {}) {
|
|
|
123
123
|
const textured = typeof seed === "number" && Number.isFinite(seed);
|
|
124
124
|
const size = _nullishCoalesce(shape.brush.size, () => ( (textured ? void 0 : _nullishCoalesce(shape.strokeWidth, () => ( 3)))));
|
|
125
125
|
const brush = resolveCanvasBrush(shape.brush.brush, size, shape.brush.spec);
|
|
126
|
-
const points =
|
|
126
|
+
const points = _chunkG5ADO47Gcjs.pathDataToPolyline.call(void 0, shape.d);
|
|
127
127
|
if (!points.length) return null;
|
|
128
128
|
const { pressure, tiltX, tiltY } = shape.brush;
|
|
129
129
|
const input = {
|