@pixerate/editor 0.275.1 → 0.277.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-editor/index.cjs +62 -62
- package/dist/animation-editor/index.js +6 -6
- package/dist/brushes/index.cjs +84 -71
- package/dist/brushes/index.js +20 -7
- package/dist/brushes/patch.d.cts +4 -4
- package/dist/brushes/patch.d.ts +4 -4
- package/dist/brushes/profiles.d.cts +4 -0
- package/dist/brushes/profiles.d.ts +4 -0
- package/dist/brushes/schema.d.cts +2 -0
- package/dist/brushes/schema.d.ts +2 -0
- package/dist/brushes/types.d.cts +32 -1
- package/dist/brushes/types.d.ts +32 -1
- package/dist/{brushstrokeRender-2RQL46WB.cjs → brushstrokeRender-7HOMKZNO.cjs} +21 -21
- package/dist/{brushstrokeRender-WGY4DZHF.js → brushstrokeRender-EIK3625T.js} +5 -5
- package/dist/canvas/index.cjs +13 -13
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-T6KNJVBP.cjs → chunk-4NZTK7LM.cjs} +35 -35
- package/dist/{chunk-LMG57L5A.js → chunk-CTHWXW5M.js} +121 -8
- package/dist/{chunk-43XJC3PP.js → chunk-DWPFGWW5.js} +1 -1
- package/dist/{chunk-MSWLJI4G.js → chunk-FNIWHSLI.js} +2 -2
- package/dist/{chunk-JDXWLCXF.cjs → chunk-H3EME3S5.cjs} +2 -2
- package/dist/{chunk-Y2ISSVPH.js → chunk-HHX7ST6Z.js} +167 -9
- package/dist/{chunk-6QNDGMK7.cjs → chunk-HRUK5TFW.cjs} +187 -45
- package/dist/{chunk-NIBFKXMD.cjs → chunk-IFWLZZCW.cjs} +619 -2
- package/dist/{chunk-W6JDUXP3.js → chunk-J23JOHPR.js} +1 -1
- package/dist/{chunk-HVO6XCI4.js → chunk-JPB65KQ6.js} +1 -1
- package/dist/{chunk-Y354ETAE.cjs → chunk-JVFGK3DK.cjs} +69 -58
- package/dist/{chunk-ACTHSQOI.js → chunk-LDZOXESA.js} +618 -1
- package/dist/{chunk-IEEY5RJV.js → chunk-LZE2QZCT.js} +1 -1
- package/dist/{chunk-DHC4E2DU.cjs → chunk-MITQGHN3.cjs} +121 -8
- package/dist/{chunk-75SYKUOT.js → chunk-N4MCWQZ7.js} +1 -1
- package/dist/{chunk-G5TCMKX5.js → chunk-NJLPW4MM.js} +17 -6
- package/dist/{chunk-STLTOISW.cjs → chunk-NPPMKMCU.cjs} +17 -17
- package/dist/{chunk-OLZXXAER.cjs → chunk-PEQWARCP.cjs} +408 -250
- package/dist/{chunk-ZM7IKQ4X.js → chunk-PPDUCZZV.js} +152 -10
- package/dist/{chunk-RAPNRK4R.cjs → chunk-QBZEC2JJ.cjs} +88 -88
- package/dist/{chunk-RJGW3RQS.js → chunk-ROURHJYT.js} +1 -1
- package/dist/{chunk-62MFUHSI.cjs → chunk-UQXULAMQ.cjs} +16 -16
- package/dist/{chunk-YYZC2EQO.cjs → chunk-VNF7A2JA.cjs} +23 -23
- package/dist/{chunk-C6VB7CPY.cjs → chunk-WFSYZO5Z.cjs} +1 -1
- package/dist/image-editor/index.cjs +880 -852
- package/dist/image-editor/index.js +36 -8
- package/dist/paint/index.cjs +33 -5
- package/dist/paint/index.d.cts +1 -0
- package/dist/paint/index.d.ts +1 -0
- package/dist/paint/index.js +32 -4
- package/dist/paint/live-patterns.d.cts +149 -0
- package/dist/paint/live-patterns.d.ts +149 -0
- package/dist/paint/wet/world.d.cts +13 -0
- package/dist/paint/wet/world.d.ts +13 -0
- package/dist/paint-editor/brush-model.d.cts +21 -1
- package/dist/paint-editor/brush-model.d.ts +21 -1
- package/dist/paint-editor/controller.d.cts +102 -1
- package/dist/paint-editor/controller.d.ts +102 -1
- package/dist/paint-editor/document.d.cts +8 -1
- package/dist/paint-editor/document.d.ts +8 -1
- package/dist/paint-editor/ferro-tools.d.cts +10 -0
- package/dist/paint-editor/ferro-tools.d.ts +10 -0
- package/dist/paint-editor/ferro.d.cts +134 -0
- package/dist/paint-editor/ferro.d.ts +134 -0
- package/dist/paint-editor/index.cjs +2291 -904
- package/dist/paint-editor/index.d.cts +2 -0
- package/dist/paint-editor/index.d.ts +2 -0
- package/dist/paint-editor/index.js +1533 -146
- package/dist/paint-editor/stroke.d.cts +14 -0
- package/dist/paint-editor/stroke.d.ts +14 -0
- package/dist/paint-editor/thick.d.cts +15 -2
- package/dist/paint-editor/thick.d.ts +15 -2
- package/dist/paint-editor/ui.d.cts +7 -1
- package/dist/paint-editor/ui.d.ts +7 -1
- package/dist/paint-editor/wet-tools.d.cts +42 -0
- package/dist/paint-editor/wet-tools.d.ts +42 -0
- package/dist/paint-editor/wet.d.cts +22 -1
- package/dist/paint-editor/wet.d.ts +22 -1
- package/dist/vector/index.cjs +1002 -1002
- package/dist/vector/index.js +7 -7
- package/package.json +1 -1
- package/skills/pixerate-brushes/SKILL.md +1 -0
- package/skills/pixerate-paint-editor/SKILL.md +11 -29
- package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
- package/skills/pixerate-paint-editor/references/ferrofluid.md +36 -0
|
@@ -19,7 +19,11 @@ var _chunkWOFXZ3XUcjs = require('./chunk-WOFXZ3XU.cjs');
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
var _chunkIFWLZZCWcjs = require('./chunk-IFWLZZCW.cjs');
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
var _chunk2VX55NFMcjs = require('./chunk-2VX55NFM.cjs');
|
|
@@ -278,6 +282,29 @@ var BRUSH_PRESETS = [
|
|
|
278
282
|
behaviour: { surface: "wet", exchange: { pickup: 0.4, mix: 0.4 }, wet: { water: 0.8, pigment: 0.45, rewet: 0.2 } },
|
|
279
283
|
colour: { default: { type: "gradient", stops: [{ offset: 0, color: "paint" }, { offset: 1, color: "secondary" }], space: "pigment", map: { by: "distance", length: 500, repeat: "mirror", unit: "px" } } }
|
|
280
284
|
},
|
|
285
|
+
// Live patterns: Gray-Scott reaction-diffusion that grows inside the wet wash and freezes as it dries.
|
|
286
|
+
{
|
|
287
|
+
id: "turing-wash",
|
|
288
|
+
name: "Turing wash",
|
|
289
|
+
kind: "mark",
|
|
290
|
+
medium: "paint",
|
|
291
|
+
variety: "watercolor",
|
|
292
|
+
tip: { shape: "round", size: 36, hardness: 0.5, spacing: 0.12 },
|
|
293
|
+
dynamics: { smoothing: 0.25, pressureOpacity: true, minSize: 0.35 },
|
|
294
|
+
behaviour: { surface: "wet", exchange: { pickup: 0.45 }, wet: { water: 0.75, pigment: 0.55, rewet: 0.2 } },
|
|
295
|
+
livePatterns: { pattern: "spots", contrast: 1.2, secondColor: "#0ea5e9", relief: 0.25 }
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: "coral-ink",
|
|
299
|
+
name: "Coral ink",
|
|
300
|
+
kind: "mark",
|
|
301
|
+
medium: "ink",
|
|
302
|
+
variety: "watercolor",
|
|
303
|
+
tip: { shape: "round", size: 22, hardness: 0.65, spacing: 0.12 },
|
|
304
|
+
dynamics: { smoothing: 0.3, pressureOpacity: true, minSize: 0.3 },
|
|
305
|
+
behaviour: { surface: "wet", exchange: { pickup: 0.35 }, wet: { water: 0.7, pigment: 0.75, rewet: 0.15 } },
|
|
306
|
+
livePatterns: { pattern: "coral", scale: 1.2, contrast: 1.3 }
|
|
307
|
+
},
|
|
281
308
|
{
|
|
282
309
|
id: "watercolor-detail",
|
|
283
310
|
name: "Watercolour rigger",
|
|
@@ -905,6 +932,29 @@ var BRUSH_PRESETS = [
|
|
|
905
932
|
tip: { shape: "round", size: 6, hardness: 0.9, spacing: 0.06 },
|
|
906
933
|
dynamics: { smoothing: 0.35, pressureSmoothing: 0.25, minSize: 0.25 },
|
|
907
934
|
mutation: {}
|
|
935
|
+
},
|
|
936
|
+
// --- Ferrofluid paint: liquid magnetic paint that rises into lit Rosensweig spikes near magnets ---
|
|
937
|
+
{
|
|
938
|
+
id: "ferrofluid",
|
|
939
|
+
name: "Ferrofluid",
|
|
940
|
+
kind: "mark",
|
|
941
|
+
medium: "paint",
|
|
942
|
+
variety: "ferrofluid",
|
|
943
|
+
tip: { shape: "round", size: 28, hardness: 0.75, flow: 0.9 },
|
|
944
|
+
dynamics: { smoothing: 0.35 },
|
|
945
|
+
ferro: { amount: 0.85 },
|
|
946
|
+
colour: { default: "#111827", palette: ["#111827", "#1e293b", "#312e81", "#0f3d3e", "#3b0764", "#451a03"] }
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
id: "ferro-ink",
|
|
950
|
+
name: "Ferro ink",
|
|
951
|
+
kind: "mark",
|
|
952
|
+
medium: "ink",
|
|
953
|
+
variety: "ferrofluid",
|
|
954
|
+
tip: { shape: "round", size: 12, spacing: 0.08 },
|
|
955
|
+
dynamics: { smoothing: 0.45, minSize: 0.25, velocitySize: -0.25 },
|
|
956
|
+
ferro: { amount: 0.7, wavelength: 12, threshold: 0.15 },
|
|
957
|
+
colour: { default: "#0f172a" }
|
|
908
958
|
}
|
|
909
959
|
];
|
|
910
960
|
|
|
@@ -1074,6 +1124,18 @@ function normalizeMutation(raw = {}, base = DEFAULT_MUTATION) {
|
|
|
1074
1124
|
fade: range(raw.fade, 0.5, 10, base.fade)
|
|
1075
1125
|
};
|
|
1076
1126
|
}
|
|
1127
|
+
var DEFAULT_FERRO = { amount: 0, wavelength: 16, threshold: 0.12 };
|
|
1128
|
+
function normalizeFerro(raw = {}, base = DEFAULT_FERRO) {
|
|
1129
|
+
if (typeof raw === "number") {
|
|
1130
|
+
return { amount: unit2(raw, base.amount), wavelength: base.wavelength, threshold: base.threshold };
|
|
1131
|
+
}
|
|
1132
|
+
const r = _nullishCoalesce(raw, () => ( {}));
|
|
1133
|
+
return {
|
|
1134
|
+
amount: unit2(r.amount, base.amount),
|
|
1135
|
+
wavelength: range(r.wavelength, 4, 64, base.wavelength),
|
|
1136
|
+
threshold: range(r.threshold, 0.01, 0.95, base.threshold)
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1077
1139
|
function normalizeImpasto(raw = {}, base = DEFAULT_IMPASTO) {
|
|
1078
1140
|
return {
|
|
1079
1141
|
tool: IMPASTO_TOOLS.includes(raw.tool) ? raw.tool : base.tool,
|
|
@@ -1152,6 +1214,12 @@ function countAndFlip(raw, base) {
|
|
|
1152
1214
|
return { ...n > 1 ? { count: n } : {}, ...flip !== "none" ? { flip } : {} };
|
|
1153
1215
|
}
|
|
1154
1216
|
var withTipGrain = (g) => g ? { tipGrain: g } : {};
|
|
1217
|
+
var withLivePatterns = (lp) => lp ? { livePatterns: lp } : {};
|
|
1218
|
+
function readLivePatterns(raw, base) {
|
|
1219
|
+
if (raw === null) return null;
|
|
1220
|
+
if (!raw || typeof raw !== "object") return base;
|
|
1221
|
+
return _chunkIFWLZZCWcjs.normalizeLivePatterns.call(void 0, raw, _nullishCoalesce(base, () => ( _chunkIFWLZZCWcjs.DEFAULT_LIVE_PATTERNS)));
|
|
1222
|
+
}
|
|
1155
1223
|
function readTipGrain(raw, base) {
|
|
1156
1224
|
if (raw === null) return null;
|
|
1157
1225
|
if (!raw || typeof raw !== "object") return base;
|
|
@@ -1250,12 +1318,12 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1250
1318
|
if (rb.dirty !== void 0) mixerIn.cleanEachStroke = !rb.dirty;
|
|
1251
1319
|
if (rb.mixing !== void 0) mixerIn.mixing = rb.mixing;
|
|
1252
1320
|
}
|
|
1253
|
-
const mixer =
|
|
1321
|
+
const mixer = _chunkIFWLZZCWcjs.normalizeMixer.call(void 0, mixerIn);
|
|
1254
1322
|
let behavior = normalizeBehavior(rb, b.behavior);
|
|
1255
1323
|
if (engine === "mixer") behavior = { ...behavior, pickup: mixer.wet, mix: mixer.mix, dirty: !mixer.cleanEachStroke, mixing: mixer.mixing };
|
|
1256
1324
|
let paint = _nullishCoalesce(b.paint, () => ( null));
|
|
1257
1325
|
if (raw.paint !== void 0) {
|
|
1258
|
-
const p =
|
|
1326
|
+
const p = _chunkIFWLZZCWcjs.readPaintSource.call(void 0, raw.paint);
|
|
1259
1327
|
paint = p && p.type === "gradient" ? p : null;
|
|
1260
1328
|
} else if (rb.multicolor !== void 0) {
|
|
1261
1329
|
const mc = normalizeMultiColor(rb.multicolor, _nullishCoalesce(paintToMultiColor(paint), () => ( DEFAULT_MULTI_COLOR)));
|
|
@@ -1275,7 +1343,7 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1275
1343
|
name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim().slice(0, 80) : b.name,
|
|
1276
1344
|
category: PAINT_BRUSH_CATEGORIES.includes(raw.category) ? raw.category : b.category,
|
|
1277
1345
|
engine,
|
|
1278
|
-
tip:
|
|
1346
|
+
tip: _chunkIFWLZZCWcjs.normalizeBrush.call(void 0, { ...b.tip, ..._nullishCoalesce(raw.tip, () => ( {})) }),
|
|
1279
1347
|
grain: unit2(raw.grain, b.grain),
|
|
1280
1348
|
pressureSmoothing: range(raw.pressureSmoothing, 0, 0.95, b.pressureSmoothing),
|
|
1281
1349
|
rotation: BRUSH_ROTATIONS.includes(raw.rotation) ? raw.rotation : b.rotation,
|
|
@@ -1283,8 +1351,8 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1283
1351
|
mixer,
|
|
1284
1352
|
loadsColor: typeof raw.loadsColor === "boolean" ? raw.loadsColor : b.loadsColor,
|
|
1285
1353
|
behavior,
|
|
1286
|
-
blender:
|
|
1287
|
-
smudge:
|
|
1354
|
+
blender: _chunkIFWLZZCWcjs.normalizeBlender.call(void 0, _nullishCoalesce(raw.blender, () => ( {})), b.blender),
|
|
1355
|
+
smudge: _chunkIFWLZZCWcjs.normalizeSmudge.call(void 0, _nullishCoalesce(raw.smudge, () => ( {})), b.smudge),
|
|
1288
1356
|
wet,
|
|
1289
1357
|
impasto: normalizeImpasto(_nullishCoalesce(raw.impasto, () => ( {})), _nullishCoalesce(b.impasto, () => ( DEFAULT_IMPASTO))),
|
|
1290
1358
|
particle: normalizeParticle(_nullishCoalesce(raw.particle, () => ( {})), _nullishCoalesce(b.particle, () => ( DEFAULT_PARTICLE))),
|
|
@@ -1296,9 +1364,11 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1296
1364
|
...withIridescence(readIridescence(raw.iridescence, _nullishCoalesce(b.iridescence, () => ( null)))),
|
|
1297
1365
|
...ghost ? { ghost } : {},
|
|
1298
1366
|
...mutation ? { mutation } : {},
|
|
1367
|
+
ferro: normalizeFerro(raw.ferro, _nullishCoalesce(b.ferro, () => ( DEFAULT_FERRO))),
|
|
1299
1368
|
stamp: readStamp(raw.stamp, _nullishCoalesce(b.stamp, () => ( null))),
|
|
1300
1369
|
spray: readSpray(raw.spray, _nullishCoalesce(b.spray, () => ( null))),
|
|
1301
1370
|
...withTipGrain(readTipGrain(raw.tipGrain, _nullishCoalesce(b.tipGrain, () => ( null)))),
|
|
1371
|
+
...withLivePatterns(readLivePatterns(raw.livePatterns, _nullishCoalesce(b.livePatterns, () => ( null)))),
|
|
1302
1372
|
paint,
|
|
1303
1373
|
...raw.builtin ? { builtin: true } : {}
|
|
1304
1374
|
};
|
|
@@ -1308,16 +1378,16 @@ var BASE_BRUSH = {
|
|
|
1308
1378
|
name: "Round",
|
|
1309
1379
|
category: "custom",
|
|
1310
1380
|
engine: "dab",
|
|
1311
|
-
tip: { ...
|
|
1381
|
+
tip: { ..._chunkIFWLZZCWcjs.DEFAULT_BRUSH },
|
|
1312
1382
|
grain: 0,
|
|
1313
1383
|
pressureSmoothing: 0,
|
|
1314
1384
|
rotation: "fixed",
|
|
1315
1385
|
dynamics: { ...DEFAULT_DYNAMICS },
|
|
1316
|
-
mixer: { ...
|
|
1386
|
+
mixer: { ..._chunkIFWLZZCWcjs.DEFAULT_MIXER },
|
|
1317
1387
|
loadsColor: true,
|
|
1318
1388
|
behavior: { ...DEFAULT_BEHAVIOR, multicolor: { ...DEFAULT_MULTI_COLOR, colors: [] } },
|
|
1319
|
-
blender: { ...
|
|
1320
|
-
smudge: { ...
|
|
1389
|
+
blender: { ..._chunkIFWLZZCWcjs.DEFAULT_BLENDER },
|
|
1390
|
+
smudge: { ..._chunkIFWLZZCWcjs.DEFAULT_SMUDGE },
|
|
1321
1391
|
wet: { ...DEFAULT_WET_BRUSH },
|
|
1322
1392
|
impasto: { ...DEFAULT_IMPASTO },
|
|
1323
1393
|
particle: { ...DEFAULT_PARTICLE },
|
|
@@ -1326,6 +1396,7 @@ var BASE_BRUSH = {
|
|
|
1326
1396
|
light: { ...DEFAULT_LIGHT },
|
|
1327
1397
|
fluorescence: { ...DEFAULT_FLUORESCENCE },
|
|
1328
1398
|
thermochromic: { ...DEFAULT_THERMOCHROMIC },
|
|
1399
|
+
ferro: { ...DEFAULT_FERRO },
|
|
1329
1400
|
stamp: null,
|
|
1330
1401
|
spray: null,
|
|
1331
1402
|
paint: null
|
|
@@ -1352,6 +1423,23 @@ var ONE_STAMP = {
|
|
|
1352
1423
|
toEngine: (v) => _nullishCoalesce(v[0], () => ( null)),
|
|
1353
1424
|
toFacet: (v) => v ? [v] : void 0
|
|
1354
1425
|
};
|
|
1426
|
+
var LIVE_PATTERNS_MAP = {
|
|
1427
|
+
toEngine: (v) => v !== null && typeof v === "object" ? JSON.parse(JSON.stringify(v)) : v,
|
|
1428
|
+
toFacet: (v) => {
|
|
1429
|
+
if (!v || typeof v !== "object") return void 0;
|
|
1430
|
+
const lp = v;
|
|
1431
|
+
const preset = _nullishCoalesce(_chunkIFWLZZCWcjs.LIVE_PATTERN_PRESETS[lp.pattern], () => ( _chunkIFWLZZCWcjs.LIVE_PATTERN_PRESETS.spots));
|
|
1432
|
+
return {
|
|
1433
|
+
pattern: lp.pattern,
|
|
1434
|
+
...lp.feed !== preset.feed ? { feed: lp.feed } : {},
|
|
1435
|
+
...lp.kill !== preset.kill ? { kill: lp.kill } : {},
|
|
1436
|
+
...lp.scale !== _chunkIFWLZZCWcjs.DEFAULT_LIVE_PATTERNS.scale ? { scale: lp.scale } : {},
|
|
1437
|
+
...lp.contrast !== _chunkIFWLZZCWcjs.DEFAULT_LIVE_PATTERNS.contrast ? { contrast: lp.contrast } : {},
|
|
1438
|
+
...lp.secondColor !== null ? { secondColor: lp.secondColor } : {},
|
|
1439
|
+
...lp.relief !== _chunkIFWLZZCWcjs.DEFAULT_LIVE_PATTERNS.relief ? { relief: lp.relief } : {}
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1355
1443
|
var f = (facet, engine, engines = ALL, extra = {}) => ({ facet, engine, engines, ...extra });
|
|
1356
1444
|
var each = (facetPrefix, enginePrefix, keys, engines) => keys.map((k) => f(`${facetPrefix}.${k}`, `${enginePrefix}.${k}`, engines));
|
|
1357
1445
|
var BRUSH_FIELDS = [
|
|
@@ -1368,6 +1456,7 @@ var BRUSH_FIELDS = [
|
|
|
1368
1456
|
...each("fluorescence", "fluorescence", ["amount", "invisible"]),
|
|
1369
1457
|
...each("thermochromic", "thermochromic", ["hotColor", "threshold"]),
|
|
1370
1458
|
...each("iridescence", "iridescence", ["thickness", "pressureToThickness", "ior"], FILM),
|
|
1459
|
+
...each("ferro", "ferro", ["amount", "wavelength", "threshold"]),
|
|
1371
1460
|
// Paint exchange: the dab, wet and impasto engines' behaviour, or the Mixer Brush's settings.
|
|
1372
1461
|
f("behaviour.exchange.pickup", "behavior.pickup", EXCHANGE),
|
|
1373
1462
|
f("behaviour.exchange.mix", "behavior.mix", EXCHANGE),
|
|
@@ -1390,6 +1479,7 @@ var BRUSH_FIELDS = [
|
|
|
1390
1479
|
f("behaviour.strength", "wet.strength", ["wet"]),
|
|
1391
1480
|
// Surfaces.
|
|
1392
1481
|
...each("behaviour.wet", "wet", ["medium", "water", "pigment", "body", "rewet", "load", "thinner"], ["wet"]),
|
|
1482
|
+
f("livePatterns", "livePatterns", ["wet"], { map: LIVE_PATTERNS_MAP }),
|
|
1393
1483
|
...each("ghost", "ghost", ["linger", "smear", "trigger"], ["wet"]),
|
|
1394
1484
|
...each("mutation", "mutation", ["settle", "patience", "fade"], ["dab", "wet", "impasto", "particle", "bristle"]),
|
|
1395
1485
|
f("behaviour.thick.applicator", "impasto.tool", ["impasto"]),
|
|
@@ -1486,6 +1576,7 @@ function resolveBrush(brush, options = {}) {
|
|
|
1486
1576
|
if (brush.iridescence && FILM.includes(engine)) input.iridescence = { ..._nullishCoalesce(input.iridescence, () => ( {})) };
|
|
1487
1577
|
if (engine === "wet") {
|
|
1488
1578
|
setPath(input, "wet.tool", brush.kind === "tool" ? WET_TOOL_OF[brush.tool] : "paint");
|
|
1579
|
+
if (brush.kind === "tool") delete input.livePatterns;
|
|
1489
1580
|
const medium = resolveWetMedium(brush);
|
|
1490
1581
|
if (medium) setPath(input, "wet.medium", medium);
|
|
1491
1582
|
if (brush.kind !== "tool" && brush.ghost) input.ghost = { ..._nullishCoalesce(input.ghost, () => ( {})) };
|
|
@@ -1574,7 +1665,7 @@ function skeleton(b) {
|
|
|
1574
1665
|
}
|
|
1575
1666
|
}
|
|
1576
1667
|
}
|
|
1577
|
-
var ORDER = ["id", "name", "description", "tags", "kind", "medium", "variety", "tool", "tip", "marks", "dynamics", "behaviour", "light", "fluorescence", "thermochromic", "iridescence", "ghost", "mutation", "motion", "colour"];
|
|
1668
|
+
var ORDER = ["id", "name", "description", "tags", "kind", "medium", "variety", "tool", "tip", "marks", "dynamics", "behaviour", "light", "fluorescence", "thermochromic", "iridescence", "ghost", "mutation", "ferro", "motion", "livePatterns", "colour"];
|
|
1578
1669
|
function migrateBrushV1(raw) {
|
|
1579
1670
|
const b = normalizePaintBrush(raw);
|
|
1580
1671
|
const brush = skeleton(b);
|
|
@@ -1621,7 +1712,7 @@ var STAMP = {
|
|
|
1621
1712
|
pattern: BRUSH_STAMP_PATTERN,
|
|
1622
1713
|
description: `A sticker ref provider:id (find_stickers lists them; the editor's own are ${_chunkRDFHWAHLcjs.STAMP_KINDS.map((k) => `editor:${k}`).join(", ")}), or an image source source:<id> (list_brush_sources lists them; add_brush_source makes one from an image). A bare editor stamp (star) means editor:star.`
|
|
1623
1714
|
};
|
|
1624
|
-
var TIP_GRAIN_PATTERNS =
|
|
1715
|
+
var TIP_GRAIN_PATTERNS = _chunkIFWLZZCWcjs.GRAIN_PATTERNS.filter((p) => p !== "smooth");
|
|
1625
1716
|
var BRUSH_GRAIN_SOURCE_PATTERN = `^(?:source:[a-z0-9][a-z0-9-]{0,63}|${TIP_GRAIN_PATTERNS.join("|")})$`;
|
|
1626
1717
|
var TIP_GRAIN = object(
|
|
1627
1718
|
"Texture inside the tip (a grain source), separate from the paper's grain: each dab is multiplied by it. Continuous and scatter marks of round and stamp tips.",
|
|
@@ -1647,7 +1738,7 @@ var TIP = object(
|
|
|
1647
1738
|
"The tip: its shape and the dab it lays.",
|
|
1648
1739
|
{
|
|
1649
1740
|
shape: oneOf2(BRUSH_TIP_SHAPES, "round, flat, chisel: an ellipse set by roundness and angle (the name is for people); bristle: a tuft of bristles (see bristle); stamp: dabs shaped like a stamp, a sticker or an image source (see stamps), also laid as sprays, objects or particles."),
|
|
1650
|
-
size: num(1,
|
|
1741
|
+
size: num(1, _chunkIFWLZZCWcjs.MAX_BRUSH_SIZE, "Diameter in px (default 20)."),
|
|
1651
1742
|
hardness: unit3("Edge hardness, 0 soft .. 1 hard (default 1). Image-source stamps fade towards their edges below 1."),
|
|
1652
1743
|
spacing: num(0.01, 10, "Dab spacing as a fraction of the diameter: 0.05 smooth, 1 dotted (default 0.1)."),
|
|
1653
1744
|
angle: num(-360, 360, "Tip angle in degrees, counter-clockwise; added to the stroke direction or barrel with dynamics.rotation (default 0)."),
|
|
@@ -1813,11 +1904,29 @@ var MUTATION = object("Mutation paint: you paint something and pause; once an ag
|
|
|
1813
1904
|
patience: num(5, 300, "Seconds to wait for an agent before giving up and keeping the paint as ordinary paint (default 45)."),
|
|
1814
1905
|
fade: num(0.5, 10, "Seconds the paint takes to sink away once claimed by read_mutation (default 2).")
|
|
1815
1906
|
});
|
|
1907
|
+
var FERRO = object("Liquid ferrofluid paint: rises into glossy Rosensweig spikes near magnets until cured.", {
|
|
1908
|
+
amount: unit3("How much liquid ferrofluid the brush lays per dab (default 0: none)."),
|
|
1909
|
+
wavelength: num(4, 64, "Capillary wavelength (spike-to-spike spacing) in pixels (default 16)."),
|
|
1910
|
+
threshold: num(0.01, 0.95, "Critical magnetic field |B| threshold where spikes begin to rise (default 0.12).")
|
|
1911
|
+
});
|
|
1816
1912
|
var MOTION = object("Animation after painting.", {
|
|
1817
1913
|
live: bool("Effect paint keeps playing on stroke layers (default false)."),
|
|
1818
1914
|
fps: int(1, 60, "Suggested playback rate in frames per second."),
|
|
1819
1915
|
waveSpeed: unit3("Particle waves: how fast they run by stroke time; 0 they stand still (default 0), 1 about four a second.")
|
|
1820
1916
|
});
|
|
1917
|
+
var LIVE_PATTERNS = object("Gray-Scott reaction-diffusion patterns (spots, stripes, coral, maze, worms) that grow inside wet paint (behaviour.surface: wet) and freeze where the paint dries.", {
|
|
1918
|
+
pattern: oneOf2(_chunkIFWLZZCWcjs.LIVE_PATTERN_KINDS, "Named Pearson feed/kill preset: spots (default, dividing mitosis spots), stripes (labyrinthine stripes), coral (branching dendrites), maze (winding channels) or worms (vermicular tracks)."),
|
|
1919
|
+
feed: num(0.01, 0.1, "Gray-Scott feed rate F (0.01..0.1; default from pattern)."),
|
|
1920
|
+
kill: num(0.03, 0.08, "Gray-Scott kill rate k (0.03..0.08; default from pattern)."),
|
|
1921
|
+
scale: num(0.5, 4, "Spatial scale of the pattern (diffusion multiplier, 0.5..4, default 1)."),
|
|
1922
|
+
contrast: num(0, 2, "Visual contrast of the V concentration field (0..2, default 1)."),
|
|
1923
|
+
secondColor: {
|
|
1924
|
+
type: ["string", "null"],
|
|
1925
|
+
pattern: "^#[0-9a-fA-F]{6}$",
|
|
1926
|
+
description: "Optional second colour (#rrggbb) that V blends the wet paint towards; null or omitted modulates pigment density/contrast."
|
|
1927
|
+
},
|
|
1928
|
+
relief: unit3("Impasto relief height written to the layer thick map as patterns grow (0..1, default 0).")
|
|
1929
|
+
});
|
|
1821
1930
|
var SOLID = {
|
|
1822
1931
|
type: "string",
|
|
1823
1932
|
pattern: "^(#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgba?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*(,\\s*(0|1|0?\\.\\d+)\\s*)?\\))$",
|
|
@@ -1847,7 +1956,7 @@ var EFFECT = object(
|
|
|
1847
1956
|
);
|
|
1848
1957
|
var PAINT = {
|
|
1849
1958
|
description: "A colour a brush can carry: a solid colour, a light / dark pair, a gradient or effect paint.",
|
|
1850
|
-
anyOf: [SOLID, DUAL,
|
|
1959
|
+
anyOf: [SOLID, DUAL, _chunkIFWLZZCWcjs.PAINT_GRADIENT_JSON_SCHEMA, EFFECT]
|
|
1851
1960
|
};
|
|
1852
1961
|
var COLOUR = object("The brush's colours. The user's colour still wins unless own is set.", {
|
|
1853
1962
|
default: {
|
|
@@ -1881,7 +1990,9 @@ var BRUSH_JSON_SCHEMA = object(
|
|
|
1881
1990
|
iridescence: IRIDESCENCE,
|
|
1882
1991
|
ghost: GHOST,
|
|
1883
1992
|
mutation: MUTATION,
|
|
1993
|
+
ferro: FERRO,
|
|
1884
1994
|
motion: MOTION,
|
|
1995
|
+
livePatterns: LIVE_PATTERNS,
|
|
1885
1996
|
colour: COLOUR
|
|
1886
1997
|
},
|
|
1887
1998
|
["id", "name", "kind", "tip"]
|
|
@@ -1898,7 +2009,9 @@ var BRUSH_FACET_SCHEMAS = {
|
|
|
1898
2009
|
iridescence: IRIDESCENCE,
|
|
1899
2010
|
ghost: GHOST,
|
|
1900
2011
|
mutation: MUTATION,
|
|
2012
|
+
ferro: FERRO,
|
|
1901
2013
|
motion: MOTION,
|
|
2014
|
+
livePatterns: LIVE_PATTERNS,
|
|
1902
2015
|
colour: COLOUR,
|
|
1903
2016
|
paint: PAINT
|
|
1904
2017
|
};
|
|
@@ -2047,17 +2160,19 @@ function read(value, schema, path, problems) {
|
|
|
2047
2160
|
problems.push(`${path} must be ${JSON.stringify(schema.const)}; dropped.`);
|
|
2048
2161
|
return void 0;
|
|
2049
2162
|
}
|
|
2050
|
-
|
|
2163
|
+
if (value === null && Array.isArray(schema.type) && schema.type.includes("null")) return null;
|
|
2164
|
+
const schemaType = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
2165
|
+
switch (schemaType) {
|
|
2051
2166
|
case "number":
|
|
2052
2167
|
case "integer": {
|
|
2053
2168
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2054
2169
|
problems.push(`${path} must be a number; dropped.`);
|
|
2055
2170
|
return void 0;
|
|
2056
2171
|
}
|
|
2057
|
-
let n =
|
|
2172
|
+
let n = schemaType === "integer" ? Math.round(value) : value;
|
|
2058
2173
|
if (schema.minimum !== void 0 && n < schema.minimum) n = schema.minimum;
|
|
2059
2174
|
if (schema.maximum !== void 0 && n > schema.maximum) n = schema.maximum;
|
|
2060
|
-
if (n !== value) problems.push(`${path} ${value} is out of range (${schema.minimum}..${schema.maximum}${
|
|
2175
|
+
if (n !== value) problems.push(`${path} ${value} is out of range (${schema.minimum}..${schema.maximum}${schemaType === "integer" ? ", whole" : ""}); set to ${n}.`);
|
|
2061
2176
|
return n;
|
|
2062
2177
|
}
|
|
2063
2178
|
case "boolean":
|
|
@@ -2070,7 +2185,8 @@ function read(value, schema, path, problems) {
|
|
|
2070
2185
|
problems.push(`${path} ${JSON.stringify(value)} is not valid${schema.enum ? `; use one of ${schema.enum.join(", ")}` : ""}; dropped.`);
|
|
2071
2186
|
return void 0;
|
|
2072
2187
|
}
|
|
2073
|
-
|
|
2188
|
+
const s = schema.pattern === "^#[0-9a-fA-F]{6}$" ? value.toLowerCase() : value;
|
|
2189
|
+
return schema.maxLength !== void 0 ? s.slice(0, schema.maxLength) : s;
|
|
2074
2190
|
}
|
|
2075
2191
|
case "array": {
|
|
2076
2192
|
if (!Array.isArray(value)) {
|
|
@@ -2117,7 +2233,7 @@ function readPaint(value, path, problems) {
|
|
|
2117
2233
|
if (typeof value === "string") {
|
|
2118
2234
|
if (SOLID2.test(value.trim())) return value.trim().toLowerCase();
|
|
2119
2235
|
} else if (isObject(value) && value.type === "gradient") {
|
|
2120
|
-
const g =
|
|
2236
|
+
const g = _chunkIFWLZZCWcjs.readPaintSource.call(void 0, value);
|
|
2121
2237
|
if (g && g.type === "gradient") return g;
|
|
2122
2238
|
} else if (isObject(value) && value.type === "effect") {
|
|
2123
2239
|
if (!_chunkWOFXZ3XUcjs.validateJsonSchema.call(void 0, value, BRUSH_FACET_SCHEMAS.paint.anyOf[3]).length) return clone(value);
|
|
@@ -2139,14 +2255,14 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2139
2255
|
else if (raw.kind === void 0) kind = tool ? "tool" : "mark";
|
|
2140
2256
|
else throw new Error(`Brush "${raw.id}" kind ${JSON.stringify(raw.kind)} is not valid: use mark (lays a material) or tool (works on paint already down).`);
|
|
2141
2257
|
if (kind === "tool" && !tool) throw new Error(`Brush "${raw.id}" is a tool and needs tool: one of ${BRUSH_TOOLS.join(", ")}.`);
|
|
2142
|
-
const { colour: rawColour, marks: rawMarks, kind: _k, id: _i, name: _n, tool: _t, ...rest } = raw;
|
|
2258
|
+
const { colour: rawColour, marks: rawMarks, ferro: rawFerro, kind: _k, id: _i, name: _n, tool: _t, ...rest } = raw;
|
|
2143
2259
|
if (typeof rest.fluorescence === "number" && Number.isFinite(rest.fluorescence)) rest.fluorescence = { amount: rest.fluorescence };
|
|
2144
2260
|
const out = readObject(rest, BRUSH_JSON_SCHEMA, "brush", problems);
|
|
2145
2261
|
if (_optionalChain([out, 'access', _29 => _29.thermochromic, 'optionalAccess', _30 => _30.hotColor])) out.thermochromic.hotColor = out.thermochromic.hotColor.toLowerCase();
|
|
2146
2262
|
const brush = { id: raw.id, name, ...out, kind, ...tool ? { tool } : {}, tip: _nullishCoalesce(out.tip, () => ( { shape: "round" })) };
|
|
2147
2263
|
const ordered = () => {
|
|
2148
|
-
const { id, name: n, description, tags, kind: k, medium, variety, tool: t, tip, marks, dynamics, behaviour, light, fluorescence, thermochromic, iridescence, ghost, mutation, motion, colour } = brush;
|
|
2149
|
-
const all = { id, name: n, description, tags, kind: k, medium, variety, tool: t, tip, marks, dynamics, behaviour, light, fluorescence, thermochromic, iridescence, ghost, mutation, motion, colour };
|
|
2264
|
+
const { id, name: n, description, tags, kind: k, medium, variety, tool: t, tip, marks, dynamics, behaviour, light, fluorescence, thermochromic, iridescence, ghost, mutation, ferro, motion, livePatterns, colour } = brush;
|
|
2265
|
+
const all = { id, name: n, description, tags, kind: k, medium, variety, tool: t, tip, marks, dynamics, behaviour, light, fluorescence, thermochromic, iridescence, ghost, mutation, ferro, motion, livePatterns, colour };
|
|
2150
2266
|
return Object.fromEntries(Object.entries(all).filter(([, v]) => v !== void 0));
|
|
2151
2267
|
};
|
|
2152
2268
|
if (!out.tip) problems.push("tip is missing; a round tip is used.");
|
|
@@ -2160,6 +2276,11 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2160
2276
|
if (!schema) problems.push(`marks.kind ${JSON.stringify(k)} is not valid; use continuous, scatter, particles or objects. marks dropped.`);
|
|
2161
2277
|
else brush.marks = readObject(rawMarks, schema, "brush.marks", problems);
|
|
2162
2278
|
}
|
|
2279
|
+
if (rawFerro !== void 0) {
|
|
2280
|
+
const ferroObj = typeof rawFerro === "number" ? { amount: rawFerro } : rawFerro;
|
|
2281
|
+
const f2 = readObject(ferroObj, BRUSH_FACET_SCHEMAS.ferro, "brush.ferro", problems);
|
|
2282
|
+
if (f2 && Object.keys(f2).length) brush.ferro = f2;
|
|
2283
|
+
}
|
|
2163
2284
|
if (rawColour !== void 0) {
|
|
2164
2285
|
const c = readObject(rawColour, { ...BRUSH_FACET_SCHEMAS.colour, properties: { own: BRUSH_FACET_SCHEMAS.colour.properties.own, default: {}, palette: {} } }, "brush.colour", problems);
|
|
2165
2286
|
if (c) {
|
|
@@ -2194,6 +2315,10 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2194
2315
|
drop("marks", "tools lay no marks");
|
|
2195
2316
|
delete brush.marks;
|
|
2196
2317
|
}
|
|
2318
|
+
if (brush.ferro) {
|
|
2319
|
+
drop("ferro", "tools lay no ferrofluid");
|
|
2320
|
+
delete brush.ferro;
|
|
2321
|
+
}
|
|
2197
2322
|
if (brush.iridescence) {
|
|
2198
2323
|
drop("iridescence", "tools lay no film");
|
|
2199
2324
|
delete brush.iridescence;
|
|
@@ -2279,8 +2404,12 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2279
2404
|
delete b.exchange;
|
|
2280
2405
|
}
|
|
2281
2406
|
}
|
|
2407
|
+
if (brush.livePatterns && (kind !== "mark" || _optionalChain([brush, 'access', _52 => _52.behaviour, 'optionalAccess', _53 => _53.surface]) !== "wet" || _optionalChain([brush, 'access', _54 => _54.marks, 'optionalAccess', _55 => _55.kind]) === "particles" || _optionalChain([brush, 'access', _56 => _56.marks, 'optionalAccess', _57 => _57.kind]) === "objects" || brush.tip.shape === "bristle")) {
|
|
2408
|
+
drop("livePatterns", "only wet paint brushes grow live patterns (set behaviour.surface to wet)");
|
|
2409
|
+
delete brush.livePatterns;
|
|
2410
|
+
}
|
|
2282
2411
|
if (brush.ghost) {
|
|
2283
|
-
if (kind === "tool" || _optionalChain([brush, 'access',
|
|
2412
|
+
if (kind === "tool" || _optionalChain([brush, 'access', _58 => _58.behaviour, 'optionalAccess', _59 => _59.surface]) !== "wet") {
|
|
2284
2413
|
drop("ghost", "it needs a mark brush with behaviour.surface wet");
|
|
2285
2414
|
delete brush.ghost;
|
|
2286
2415
|
} else {
|
|
@@ -2288,7 +2417,7 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2288
2417
|
drop("light", "ghost paint uses the wet light channel as its tracer");
|
|
2289
2418
|
delete brush.light;
|
|
2290
2419
|
}
|
|
2291
|
-
if (_optionalChain([brush, 'access',
|
|
2420
|
+
if (_optionalChain([brush, 'access', _60 => _60.behaviour, 'optionalAccess', _61 => _61.exchange])) {
|
|
2292
2421
|
drop("behaviour.exchange", "ghost paint does not pick paint up");
|
|
2293
2422
|
delete brush.behaviour.exchange;
|
|
2294
2423
|
}
|
|
@@ -2318,7 +2447,9 @@ var PAINT_PROFILE = {
|
|
|
2318
2447
|
tilt: "full",
|
|
2319
2448
|
tipGrain: "full",
|
|
2320
2449
|
dabCount: "full",
|
|
2321
|
-
|
|
2450
|
+
livePatterns: "full",
|
|
2451
|
+
iridescence: "full",
|
|
2452
|
+
ferro: "full"
|
|
2322
2453
|
};
|
|
2323
2454
|
var IMAGE_PROFILE = {
|
|
2324
2455
|
id: "image",
|
|
@@ -2386,28 +2517,30 @@ function checkBrush(brush, profile) {
|
|
|
2386
2517
|
const image = brush.tip.shape === "stamp" && (_nullishCoalesce(brush.tip.stamps, () => ( []))).some(isBrushSourceRef);
|
|
2387
2518
|
add("tip.shape", "error", `${host} can't lay a ${image ? "stamp tip (image tips are stamps)" : `${brush.tip.shape} tip`}.`, `Set tip.shape to one of: ${list(profile.tipShapes)}.`);
|
|
2388
2519
|
}
|
|
2389
|
-
const marks = _nullishCoalesce(_optionalChain([brush, 'access',
|
|
2520
|
+
const marks = _nullishCoalesce(_optionalChain([brush, 'access', _62 => _62.marks, 'optionalAccess', _63 => _63.kind]), () => ( "continuous"));
|
|
2390
2521
|
if (!profile.marks.includes(marks)) add("marks", "error", `${host} can't lay ${marks} marks.`, `Use marks ${list(profile.marks)} (remove marks for continuous dabs).`);
|
|
2391
|
-
const surface = _nullishCoalesce(_optionalChain([brush, 'access',
|
|
2522
|
+
const surface = _nullishCoalesce(_optionalChain([brush, 'access', _64 => _64.behaviour, 'optionalAccess', _65 => _65.surface]), () => ( "dry"));
|
|
2392
2523
|
if (brush.kind === "mark" && !profile.surfaces.includes(surface)) add("behaviour.surface", "error", `${host} has no ${surface} surface.`, `Set behaviour.surface to ${list(profile.surfaces)}.`);
|
|
2393
|
-
const ex = _optionalChain([brush, 'access',
|
|
2394
|
-
feature(profile.reservoir, !!_optionalChain([ex, 'optionalAccess',
|
|
2395
|
-
feature(profile.exchange, !_optionalChain([ex, 'optionalAccess',
|
|
2396
|
-
feature(profile.grain, nonZero(_optionalChain([brush, 'access',
|
|
2397
|
-
feature(profile.light, nonZero(_optionalChain([brush, 'access',
|
|
2524
|
+
const ex = _optionalChain([brush, 'access', _66 => _66.behaviour, 'optionalAccess', _67 => _67.exchange]);
|
|
2525
|
+
feature(profile.reservoir, !!_optionalChain([ex, 'optionalAccess', _68 => _68.reservoir]), "behaviour.exchange.reservoir", "a Mixer Brush reservoir", "Remove behaviour.exchange.reservoir.");
|
|
2526
|
+
feature(profile.exchange, !_optionalChain([ex, 'optionalAccess', _69 => _69.reservoir]) && (nonZero(_optionalChain([ex, 'optionalAccess', _70 => _70.pickup])) || !!_optionalChain([ex, 'optionalAccess', _71 => _71.dirty])), "behaviour.exchange", "picking paint up", "Remove behaviour.exchange.");
|
|
2527
|
+
feature(profile.grain, nonZero(_optionalChain([brush, 'access', _72 => _72.behaviour, 'optionalAccess', _73 => _73.grain])), "behaviour.grain", "paper grain", "Set behaviour.grain to 0.");
|
|
2528
|
+
feature(profile.light, nonZero(_optionalChain([brush, 'access', _74 => _74.light, 'optionalAccess', _75 => _75.amount])), "light", "light paint (glow and spill)", "Remove light (or set light.amount to 0).");
|
|
2529
|
+
feature(_nullishCoalesce(profile.livePatterns, () => ( "none")), Boolean(brush.livePatterns), "livePatterns", "live reaction-diffusion patterns", "Remove livePatterns.");
|
|
2398
2530
|
const smartInks = profile.id === "paint" ? "full" : "none";
|
|
2399
|
-
feature(smartInks, nonZero(_optionalChain([brush, 'access',
|
|
2400
|
-
feature(smartInks, Boolean(_optionalChain([brush, 'access',
|
|
2531
|
+
feature(smartInks, nonZero(_optionalChain([brush, 'access', _76 => _76.fluorescence, 'optionalAccess', _77 => _77.amount])), "fluorescence", "UV-fluorescent ink", "Remove fluorescence (or set fluorescence.amount to 0).");
|
|
2532
|
+
feature(smartInks, Boolean(_optionalChain([brush, 'access', _78 => _78.thermochromic, 'optionalAccess', _79 => _79.hotColor])), "thermochromic", "thermochromic ink", "Remove thermochromic.");
|
|
2401
2533
|
feature(_nullishCoalesce(profile.iridescence, () => ( "none")), !!brush.iridescence, "iridescence", "thin-film iridescence", "Remove iridescence.");
|
|
2402
2534
|
feature(profile.surfaces.includes("wet") && profile.light === "full" ? "full" : "none", !!brush.ghost, "ghost", "ghost paint", "Remove ghost.");
|
|
2403
2535
|
if (brush.mutation) {
|
|
2404
2536
|
if (profile.id !== "paint") feature("none", true, "mutation", "mutation paint", "Remove mutation.");
|
|
2405
2537
|
else if (brush.kind === "tool") add("mutation", "error", "Tools work on paint already down and cannot be mutation brushes.", "Use a mark brush, or remove mutation.");
|
|
2406
|
-
else if (_optionalChain([brush, 'access',
|
|
2407
|
-
else if (_optionalChain([ex, 'optionalAccess',
|
|
2408
|
-
else if (nonZero(_optionalChain([ex, 'optionalAccess',
|
|
2409
|
-
else if (surface === "wet" && nonZero(_optionalChain([brush, 'access',
|
|
2538
|
+
else if (_optionalChain([brush, 'access', _80 => _80.marks, 'optionalAccess', _81 => _81.kind]) === "objects") add("mutation", "error", "Sticker brushes place objects rather than paint strokes and cannot be mutation brushes.", "Remove marks (or use continuous marks), or remove mutation.");
|
|
2539
|
+
else if (_optionalChain([ex, 'optionalAccess', _82 => _82.reservoir])) add("mutation", "error", "A Mixer Brush reads paint from the layer beneath and cannot be a mutation brush.", "Remove behaviour.exchange.reservoir, or remove mutation.");
|
|
2540
|
+
else if (nonZero(_optionalChain([ex, 'optionalAccess', _83 => _83.pickup])) || !!_optionalChain([ex, 'optionalAccess', _84 => _84.dirty])) add("mutation", "error", "Mutation brushes must lay their own colour without picking up paint from beneath.", "Remove behaviour.exchange (or set pickup to 0 and dirty to false), or remove mutation.");
|
|
2541
|
+
else if (surface === "wet" && nonZero(_optionalChain([brush, 'access', _85 => _85.light, 'optionalAccess', _86 => _86.amount]))) add("mutation", "error", "Wet mutation brushes use the wet light channel as their fade tracer.", "Remove light (or set light.amount to 0), or remove mutation.");
|
|
2410
2542
|
}
|
|
2543
|
+
feature(_nullishCoalesce(profile.ferro, () => ( "none")), nonZero(_optionalChain([brush, 'access', _87 => _87.ferro, 'optionalAccess', _88 => _88.amount])), "ferro", "ferrofluid spikes and magnets", "Remove ferro (or set ferro.amount to 0).");
|
|
2411
2544
|
const m = brush.motion;
|
|
2412
2545
|
feature(profile.motion, !!m && (!!m.live || nonZero(m.waveSpeed) || m.fps !== void 0), "motion", "animation", "Remove motion.");
|
|
2413
2546
|
const d = _nullishCoalesce(brush.dynamics, () => ( {}));
|
|
@@ -2416,16 +2549,16 @@ function checkBrush(brush, profile) {
|
|
|
2416
2549
|
feature(profile.velocity, nonZero(d.velocitySize) || nonZero(d.velocityOpacity), "dynamics.velocity", "velocity dynamics (its strokes carry no timing)", "Set dynamics.velocitySize and velocityOpacity to 0.");
|
|
2417
2550
|
feature(profile.tilt, nonZero(d.tiltSize) || nonZero(d.tiltRoundness) || nonZero(d.tiltOpacity), "dynamics.tilt", "tilt dynamics", "Set dynamics.tiltSize, tiltRoundness and tiltOpacity to 0.");
|
|
2418
2551
|
const colour = brush.colour;
|
|
2419
|
-
if (_optionalChain([colour, 'optionalAccess',
|
|
2552
|
+
if (_optionalChain([colour, 'optionalAccess', _89 => _89.default]) !== void 0) {
|
|
2420
2553
|
const kind = brushColourKind(colour.default);
|
|
2421
2554
|
if (!profile.colours.includes(kind)) add("colour.default", "error", `${host} can't paint ${kind} colours.`, `Use a ${list(profile.colours)} colour, or remove colour.default.`);
|
|
2422
2555
|
}
|
|
2423
|
-
const foreign = [...new Set((_nullishCoalesce(_optionalChain([colour, 'optionalAccess',
|
|
2556
|
+
const foreign = [...new Set((_nullishCoalesce(_optionalChain([colour, 'optionalAccess', _90 => _90.palette]), () => ( []))).map(brushColourKind).filter((k) => !profile.colours.includes(k)))];
|
|
2424
2557
|
if (foreign.length) add("colour.palette", "warning", `${host} skips the palette's ${list(foreign)} colours.`, `Nothing to do; or keep to ${list(profile.colours)} colours.`);
|
|
2425
2558
|
return { ok: !issues.some((i) => i.severity === "error"), issues };
|
|
2426
2559
|
}
|
|
2427
2560
|
function withKeptIssues(check, before) {
|
|
2428
|
-
const had = new Set((_nullishCoalesce(_optionalChain([before, 'optionalAccess',
|
|
2561
|
+
const had = new Set((_nullishCoalesce(_optionalChain([before, 'optionalAccess', _91 => _91.issues]), () => ( []))).filter((i) => i.severity === "error").map((i) => i.facet));
|
|
2429
2562
|
const kept = check.issues.filter((i) => i.severity === "error" && had.has(i.facet));
|
|
2430
2563
|
const issues = check.issues.filter((i) => !kept.includes(i));
|
|
2431
2564
|
return { ok: !issues.some((i) => i.severity === "error"), issues, kept };
|
|
@@ -2458,8 +2591,12 @@ function degrade(brush, profile, options = {}) {
|
|
|
2458
2591
|
delete b.behaviour.surface;
|
|
2459
2592
|
delete b.behaviour.wet;
|
|
2460
2593
|
delete b.behaviour.thick;
|
|
2594
|
+
delete b.livePatterns;
|
|
2461
2595
|
delete b.ghost;
|
|
2462
2596
|
break;
|
|
2597
|
+
case "livePatterns":
|
|
2598
|
+
delete b.livePatterns;
|
|
2599
|
+
break;
|
|
2463
2600
|
case "ghost":
|
|
2464
2601
|
delete b.ghost;
|
|
2465
2602
|
break;
|
|
@@ -2493,6 +2630,9 @@ function degrade(brush, profile, options = {}) {
|
|
|
2493
2630
|
case "iridescence":
|
|
2494
2631
|
delete b.iridescence;
|
|
2495
2632
|
break;
|
|
2633
|
+
case "ferro":
|
|
2634
|
+
delete b.ferro;
|
|
2635
|
+
break;
|
|
2496
2636
|
case "motion":
|
|
2497
2637
|
delete b.motion;
|
|
2498
2638
|
break;
|
|
@@ -2517,7 +2657,7 @@ function degrade(brush, profile, options = {}) {
|
|
|
2517
2657
|
}
|
|
2518
2658
|
dropped.push(issue);
|
|
2519
2659
|
}
|
|
2520
|
-
if (_optionalChain([b, 'access',
|
|
2660
|
+
if (_optionalChain([b, 'access', _92 => _92.behaviour, 'optionalAccess', _93 => _93.exchange]) && !Object.keys(b.behaviour.exchange).length) delete b.behaviour.exchange;
|
|
2521
2661
|
if (b.colour && !Object.keys(b.colour).length) delete b.colour;
|
|
2522
2662
|
const after = checkBrush(b, profile).issues.filter((i) => i.severity === "error");
|
|
2523
2663
|
return { brush: b, dropped, issues: after };
|
|
@@ -2561,7 +2701,7 @@ function parseBrushLibrary2(raw, problems = []) {
|
|
|
2561
2701
|
if (file && file.version !== void 0 && file.version !== 1 && file.version !== 2) {
|
|
2562
2702
|
throw new BrushLibraryError("invalid", `Brush library version ${JSON.stringify(file.version)} is not supported; this library reads versions 1 and 2.`);
|
|
2563
2703
|
}
|
|
2564
|
-
const v1File = _optionalChain([file, 'optionalAccess',
|
|
2704
|
+
const v1File = _optionalChain([file, 'optionalAccess', _94 => _94.version]) === 1;
|
|
2565
2705
|
const out = [];
|
|
2566
2706
|
items.forEach((item, i) => {
|
|
2567
2707
|
const at = `brushes[${i}]`;
|
|
@@ -2822,7 +2962,7 @@ function snapshotBrushLibrary(library, onChange) {
|
|
|
2822
2962
|
(list2) => {
|
|
2823
2963
|
entries = list2;
|
|
2824
2964
|
user = list2.filter((e) => !e.builtin).map((e) => e.brush);
|
|
2825
|
-
_optionalChain([onChange, 'optionalCall',
|
|
2965
|
+
_optionalChain([onChange, 'optionalCall', _95 => _95()]);
|
|
2826
2966
|
},
|
|
2827
2967
|
() => void 0
|
|
2828
2968
|
);
|
|
@@ -2964,4 +3104,6 @@ function snapshotBrushLibrary(library, onChange) {
|
|
|
2964
3104
|
|
|
2965
3105
|
|
|
2966
3106
|
|
|
2967
|
-
|
|
3107
|
+
|
|
3108
|
+
|
|
3109
|
+
exports.BRUSH_PRESETS = BRUSH_PRESETS; exports.MAX_STICKER_STAMPS = MAX_STICKER_STAMPS; exports.DEFAULT_STICKER = DEFAULT_STICKER; exports.normalizeStickerSettings = normalizeStickerSettings; exports.PARTICLE_KINDS = PARTICLE_KINDS; exports.PARTICLE_RENDERS = PARTICLE_RENDERS; exports.RIBBON_PAIRS = RIBBON_PAIRS; exports.DOT_TEXTURE_ORDERS = DOT_TEXTURE_ORDERS; exports.DOT_ROTATIONS = DOT_ROTATIONS; exports.MAX_DOT_TEXTURES = MAX_DOT_TEXTURES; exports.DEFAULT_PARTICLE = DEFAULT_PARTICLE; exports.normalizeParticle = normalizeParticle; exports.DEFAULT_BRISTLE = DEFAULT_BRISTLE; exports.normalizeBristle = normalizeBristle; exports.PAINT_BRUSH_ENGINES = PAINT_BRUSH_ENGINES; exports.PAINT_BRUSH_CATEGORIES = PAINT_BRUSH_CATEGORIES; exports.WET_TOOLS = WET_TOOLS; exports.WET_MEDIA = WET_MEDIA; exports.DEFAULT_WET_BRUSH = DEFAULT_WET_BRUSH; exports.normalizeWetBrush = normalizeWetBrush; exports.IMPASTO_TOOLS = IMPASTO_TOOLS; exports.DEFAULT_IMPASTO = DEFAULT_IMPASTO; exports.DEFAULT_LIGHT = DEFAULT_LIGHT; exports.normalizeLight = normalizeLight; exports.DEFAULT_FLUORESCENCE = DEFAULT_FLUORESCENCE; exports.normalizeFluorescence = normalizeFluorescence; exports.DEFAULT_THERMOCHROMIC = DEFAULT_THERMOCHROMIC; exports.normalizeThermochromic = normalizeThermochromic; exports.DEFAULT_IRIDESCENCE = DEFAULT_IRIDESCENCE; exports.GHOST_TRIGGERS = GHOST_TRIGGERS; exports.DEFAULT_GHOST = DEFAULT_GHOST; exports.normalizeGhost = normalizeGhost; exports.DEFAULT_MUTATION = DEFAULT_MUTATION; exports.normalizeMutation = normalizeMutation; exports.DEFAULT_FERRO = DEFAULT_FERRO; exports.normalizeFerro = normalizeFerro; exports.normalizeImpasto = normalizeImpasto; exports.MULTI_COLOR_MODES = MULTI_COLOR_MODES; exports.DEFAULT_MULTI_COLOR = DEFAULT_MULTI_COLOR; exports.DEFAULT_BEHAVIOR = DEFAULT_BEHAVIOR; exports.BRUSH_ROTATIONS = BRUSH_ROTATIONS; exports.DAB_FLIPS = DAB_FLIPS; exports.TIP_GRAIN_MODES = TIP_GRAIN_MODES; exports.DEFAULT_TIP_GRAIN = DEFAULT_TIP_GRAIN; exports.DEFAULT_DYNAMICS = DEFAULT_DYNAMICS; exports.normalizeDynamics = normalizeDynamics; exports.BRUSH_ID = BRUSH_ID; exports.normalizeMultiColor = normalizeMultiColor; exports.normalizeBehavior = normalizeBehavior; exports.DEFAULT_SPRAY = DEFAULT_SPRAY; exports.isWetCapable = isWetCapable; exports.isWetBrush = isWetBrush; exports.usesPaintExchange = usesPaintExchange; exports.brushTakesPaint = brushTakesPaint; exports.paintToMultiColor = paintToMultiColor; exports.multiColorToPaint = multiColorToPaint; exports.normalizePaintBrush = normalizePaintBrush; exports.BRUSH_FIELDS = BRUSH_FIELDS; exports.getPath = getPath; exports.setPath = setPath; exports.resolveEngine = resolveEngine; exports.resolveWetMedium = resolveWetMedium; exports.resolveCategory = resolveCategory; exports.resolveBrush = resolveBrush; exports.BUILTIN_BRUSHES = BUILTIN_BRUSHES; exports.DEFAULT_BRUSH_ID = DEFAULT_BRUSH_ID; exports.brushToJSON = brushToJSON; exports.parseBrushLibrary = parseBrushLibrary; exports.migrateBrushV1 = migrateBrushV1; exports.BRUSH_KINDS = BRUSH_KINDS; exports.BRUSH_MEDIA = BRUSH_MEDIA; exports.BRUSH_TOOLS = BRUSH_TOOLS; exports.BRUSH_TIP_SHAPES = BRUSH_TIP_SHAPES; exports.BRUSH_MARKS_KINDS = BRUSH_MARKS_KINDS; exports.BRUSH_SURFACES = BRUSH_SURFACES; exports.BRUSH_GRAIN_MODES = BRUSH_GRAIN_MODES; exports.BRUSH_FLIPS = BRUSH_FLIPS; exports.GHOST_TRIGGERS2 = GHOST_TRIGGERS2; exports.MAX_BRUSH_PALETTE = MAX_BRUSH_PALETTE; exports.BRUSH_WORD = BRUSH_WORD; exports.TIP_GRAIN_PATTERNS = TIP_GRAIN_PATTERNS; exports.BRUSH_GRAIN_SOURCE_PATTERN = BRUSH_GRAIN_SOURCE_PATTERN; exports.BRUSH_JSON_SCHEMA = BRUSH_JSON_SCHEMA; exports.BRUSH_FACET_SCHEMAS = BRUSH_FACET_SCHEMAS; exports.BRUSH_SOURCE_PROVIDER = BRUSH_SOURCE_PROVIDER; exports.MAX_BRUSH_SOURCE_SIZE = MAX_BRUSH_SOURCE_SIZE; exports.MAX_BRUSH_SOURCE_INPUT_PIXELS = MAX_BRUSH_SOURCE_INPUT_PIXELS; exports.MAX_BRUSH_SOURCES = MAX_BRUSH_SOURCES; exports.BRUSH_SOURCE_CHANNELS = BRUSH_SOURCE_CHANNELS; exports.BrushSourceError = BrushSourceError; exports.brushSourceRef = brushSourceRef; exports.isBrushSourceRef = isBrushSourceRef; exports.brushSourceIdOf = brushSourceIdOf; exports.brushSourceIds = brushSourceIds; exports.summarizeBrushSource = summarizeBrushSource; exports.brushSourceIdFromName = brushSourceIdFromName; exports.brushSourceFromMask = brushSourceFromMask; exports.makeBrushSource = makeBrushSource; exports.readBrushSource = readBrushSource; exports.decodeBrushSource = decodeBrushSource; exports.WET_BRUSH_TOOLS = WET_BRUSH_TOOLS; exports.normalizeBrushSpec = normalizeBrushSpec; exports.PAINT_PROFILE = PAINT_PROFILE; exports.IMAGE_PROFILE = IMAGE_PROFILE; exports.CANVAS_PROFILE = CANVAS_PROFILE; exports.VECTOR_PROFILE = VECTOR_PROFILE; exports.BRUSH_HOST_PROFILES = BRUSH_HOST_PROFILES; exports.brushColourKind = brushColourKind; exports.checkBrush = checkBrush; exports.withKeptIssues = withKeptIssues; exports.checkBrushEdit = checkBrushEdit; exports.listPresets = listPresets; exports.degrade = degrade; exports.BrushLibraryError = BrushLibraryError; exports.isBrushLibrary = isBrushLibrary; exports.parseBrushLibrarySources = parseBrushLibrarySources; exports.parseBrushLibrary2 = parseBrushLibrary2; exports.createBrushLibrary = createBrushLibrary; exports.closest = closest; exports.snapshotBrushLibrary = snapshotBrushLibrary;
|