@pixerate/editor 0.281.0 → 0.282.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.
Files changed (57) hide show
  1. package/dist/animation-editor/index.cjs +59 -59
  2. package/dist/animation-editor/index.js +3 -3
  3. package/dist/brushes/index.cjs +76 -71
  4. package/dist/brushes/index.js +13 -8
  5. package/dist/brushes/schema.d.cts +1 -0
  6. package/dist/brushes/schema.d.ts +1 -0
  7. package/dist/brushes/types.d.cts +29 -3
  8. package/dist/brushes/types.d.ts +29 -3
  9. package/dist/{brushstrokeRender-EIK3625T.js → brushstrokeRender-4MA5CBW3.js} +3 -3
  10. package/dist/{brushstrokeRender-7HOMKZNO.cjs → brushstrokeRender-JVKVKWDA.cjs} +15 -15
  11. package/dist/canvas/index.cjs +13 -13
  12. package/dist/canvas/index.js +1 -1
  13. package/dist/{chunk-NJDEKU5D.js → chunk-2YGYYVSG.js} +1 -1
  14. package/dist/{chunk-JVFGK3DK.cjs → chunk-3JUKN3RM.cjs} +76 -53
  15. package/dist/{chunk-HN57I7KT.js → chunk-CG3FBNXE.js} +1 -1
  16. package/dist/{chunk-WFSYZO5Z.cjs → chunk-DFZ4I7EZ.cjs} +1 -1
  17. package/dist/{chunk-LZE2QZCT.js → chunk-IHMMHZGP.js} +1 -1
  18. package/dist/{chunk-K4ANWBF7.cjs → chunk-IJQ2YH2M.cjs} +35 -35
  19. package/dist/{chunk-HRUK5TFW.cjs → chunk-IUKPQCYQ.cjs} +164 -71
  20. package/dist/{chunk-WCC4NXWY.cjs → chunk-JD63B33V.cjs} +86 -86
  21. package/dist/{chunk-NJLPW4MM.js → chunk-KV7QHPKL.js} +27 -4
  22. package/dist/{chunk-H3EME3S5.cjs → chunk-MRIAR5DY.cjs} +2 -2
  23. package/dist/{chunk-ROURHJYT.js → chunk-NEDS6DSC.js} +1 -1
  24. package/dist/{chunk-FKO3CY2C.js → chunk-ULPXJ3NB.js} +1 -1
  25. package/dist/{chunk-WZYRYYXN.cjs → chunk-UOM5S4W5.cjs} +12 -12
  26. package/dist/{chunk-YOM76H2V.js → chunk-VEX6YVNG.js} +1 -1
  27. package/dist/{chunk-MNHP3AQT.cjs → chunk-WEXWDBUF.cjs} +23 -23
  28. package/dist/{chunk-PPDUCZZV.js → chunk-YRGTQLDN.js} +122 -29
  29. package/dist/image-editor/index.cjs +788 -788
  30. package/dist/image-editor/index.js +4 -4
  31. package/dist/paint-editor/antimatter.d.cts +154 -0
  32. package/dist/paint-editor/antimatter.d.ts +154 -0
  33. package/dist/paint-editor/brush-controls.d.cts +1 -0
  34. package/dist/paint-editor/brush-controls.d.ts +1 -0
  35. package/dist/paint-editor/brush-model.d.cts +9 -5
  36. package/dist/paint-editor/brush-model.d.ts +9 -5
  37. package/dist/paint-editor/controller.d.cts +23 -0
  38. package/dist/paint-editor/controller.d.ts +23 -0
  39. package/dist/paint-editor/index.cjs +1931 -891
  40. package/dist/paint-editor/index.d.cts +1 -0
  41. package/dist/paint-editor/index.d.ts +1 -0
  42. package/dist/paint-editor/index.js +1098 -58
  43. package/dist/paint-editor/mark-settings.d.cts +30 -0
  44. package/dist/paint-editor/mark-settings.d.ts +30 -0
  45. package/dist/paint-editor/stroke.d.cts +16 -0
  46. package/dist/paint-editor/stroke.d.ts +16 -0
  47. package/dist/paint-editor/strokes.d.cts +2 -0
  48. package/dist/paint-editor/strokes.d.ts +2 -0
  49. package/dist/paint-editor/ui.d.cts +4 -2
  50. package/dist/paint-editor/ui.d.ts +4 -2
  51. package/dist/vector/index.cjs +999 -999
  52. package/dist/vector/index.js +4 -4
  53. package/package.json +1 -1
  54. package/skills/pixerate-brushes/SKILL.md +1 -1
  55. package/skills/pixerate-paint-editor/SKILL.md +8 -2
  56. package/skills/pixerate-paint-editor/references/antimatter.md +49 -0
  57. package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
@@ -955,11 +955,47 @@ var BRUSH_PRESETS = [
955
955
  dynamics: { smoothing: 0.45, minSize: 0.25, velocitySize: -0.25 },
956
956
  ferro: { amount: 0.7, wavelength: 12, threshold: 0.15 },
957
957
  colour: { default: "#0f172a" }
958
+ },
959
+ // --- Antimatter paint: blasts that invert, scorch and destroy paint across visible layers ---
960
+ {
961
+ id: "antimatter",
962
+ name: "Antimatter",
963
+ kind: "mark",
964
+ medium: "paint",
965
+ variety: "antimatter",
966
+ tip: { shape: "round", size: 24, hardness: 0.75, spacing: 0.12 },
967
+ marks: { kind: "antimatter" },
968
+ dynamics: { smoothing: 0.25, minSize: 0.4 },
969
+ colour: { default: "#38bdf8", palette: ["#38bdf8", "#f472b6", "#a855f7", "#fb923c", "#4ade80"] }
970
+ },
971
+ {
972
+ id: "antimatter-flare",
973
+ name: "Antimatter flare",
974
+ kind: "mark",
975
+ medium: "paint",
976
+ variety: "antimatter",
977
+ tip: { shape: "round", size: 18, hardness: 0.7, spacing: 0.08 },
978
+ marks: { kind: "antimatter", blastRadius: 2, blastSpacing: 1.4, invert: 0.75, invertMode: "oklab", scorch: 0.35, annihilate: 0.15, sparks: 0.95, sparkReach: 5, crater: 0.3 },
979
+ dynamics: { smoothing: 0.2, minSize: 0.4 },
980
+ light: { amount: 0.9 },
981
+ colour: { default: "#f472b6", palette: ["#f472b6", "#38bdf8", "#facc15", "#a855f7", "#4ade80"] }
982
+ },
983
+ {
984
+ id: "antimatter-charge",
985
+ name: "Antimatter charge",
986
+ kind: "mark",
987
+ medium: "paint",
988
+ variety: "antimatter",
989
+ tip: { shape: "round", size: 32, hardness: 0.85, spacing: 0.15 },
990
+ marks: { kind: "antimatter", blastRadius: 4.5, blastSpacing: 4.5, threshold: 0.2, invertMode: "oklab", scorch: 0.8, annihilate: 0.6, sparks: 0.65, sparkReach: 4, crater: 0.85, duration: 1 },
991
+ dynamics: { smoothing: 0.3, minSize: 0.5 },
992
+ colour: { default: "#fb923c", palette: ["#fb923c", "#ef4444", "#38bdf8", "#a855f7"] }
958
993
  }
959
994
  ];
960
995
 
961
996
  // src/paint-editor/mark-settings.ts
962
997
  var unit = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
998
+ var range = (v, lo, hi, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : d;
963
999
  var oneOf = (v, all, d) => all.includes(v) ? v : d;
964
1000
  var MAX_STICKER_STAMPS = 16;
965
1001
  var DEFAULT_STICKER = { stamps: ["editor:star"], density: 0.4, sizeJitter: 0.4, rotationJitter: 1, scatter: 0.5, colorJitter: 0.2 };
@@ -1051,9 +1087,38 @@ function normalizeBristle(raw = {}, base = DEFAULT_BRISTLE) {
1051
1087
  jitter: unit(raw.jitter, base.jitter)
1052
1088
  };
1053
1089
  }
1090
+ var INVERT_MODES = ["rgb", "oklab"];
1091
+ var DEFAULT_ANTIMATTER = {
1092
+ blastRadius: 2.5,
1093
+ blastSpacing: 2,
1094
+ threshold: 0.1,
1095
+ invert: 1,
1096
+ invertMode: "rgb",
1097
+ scorch: 0.6,
1098
+ annihilate: 0.25,
1099
+ sparks: 0.5,
1100
+ sparkReach: 3,
1101
+ crater: 0.5,
1102
+ duration: 0.7
1103
+ };
1104
+ function normalizeAntimatter(raw = {}, base = DEFAULT_ANTIMATTER) {
1105
+ return {
1106
+ blastRadius: range(raw.blastRadius, 1, 6, base.blastRadius),
1107
+ blastSpacing: range(raw.blastSpacing, 0.5, 8, base.blastSpacing),
1108
+ threshold: unit(raw.threshold, base.threshold),
1109
+ invert: unit(raw.invert, base.invert),
1110
+ invertMode: oneOf(raw.invertMode, INVERT_MODES, base.invertMode),
1111
+ scorch: unit(raw.scorch, base.scorch),
1112
+ annihilate: unit(raw.annihilate, base.annihilate),
1113
+ sparks: unit(raw.sparks, base.sparks),
1114
+ sparkReach: range(raw.sparkReach, 0.5, 8, base.sparkReach),
1115
+ crater: unit(raw.crater, base.crater),
1116
+ duration: range(raw.duration, 0, 2, base.duration)
1117
+ };
1118
+ }
1054
1119
 
1055
1120
  // src/paint-editor/brush-model.ts
1056
- var PAINT_BRUSH_ENGINES = ["dab", "eraser", "mixer", "blender", "smudge", "wet", "impasto", "particle", "bristle", "sticker"];
1121
+ var PAINT_BRUSH_ENGINES = ["dab", "eraser", "mixer", "blender", "smudge", "wet", "impasto", "particle", "bristle", "sticker", "antimatter"];
1057
1122
  var PAINT_BRUSH_CATEGORIES = ["pencil", "pastel", "charcoal", "airbrush", "marker", "ink", "paint", "eraser", "blender", "smudge", "mixer", "watercolor", "wet-tool", "oil", "impasto", "procedural", "bristle", "sticker", "light", "custom"];
1058
1123
  var WET_TOOLS = ["paint", "water", "dry", "blow", "blend", "smudge", "blot"];
1059
1124
  var WET_MEDIA = ["watercolor", "gouache", "oil"];
@@ -1090,7 +1155,7 @@ function normalizeThermochromic(raw = {}, base = DEFAULT_THERMOCHROMIC) {
1090
1155
  const hotColor = raw.hotColor === null ? null : typeof raw.hotColor === "string" && HEX.test(raw.hotColor.trim()) ? raw.hotColor.trim().toLowerCase() : base.hotColor;
1091
1156
  return {
1092
1157
  hotColor,
1093
- threshold: range(raw.threshold, 0.05, 0.95, base.threshold)
1158
+ threshold: range2(raw.threshold, 0.05, 0.95, base.threshold)
1094
1159
  };
1095
1160
  }
1096
1161
  var DEFAULT_IRIDESCENCE = { thickness: 400, pressureToThickness: 0, ior: 1.5 };
@@ -1101,9 +1166,9 @@ function readIridescence(raw, base) {
1101
1166
  const r = raw;
1102
1167
  const b = _nullishCoalesce(base, () => ( DEFAULT_IRIDESCENCE));
1103
1168
  return {
1104
- thickness: range(r.thickness, 100, 1e3, b.thickness),
1169
+ thickness: range2(r.thickness, 100, 1e3, b.thickness),
1105
1170
  pressureToThickness: unit2(r.pressureToThickness, b.pressureToThickness),
1106
- ior: range(r.ior, 1.2, 2, b.ior)
1171
+ ior: range2(r.ior, 1.2, 2, b.ior)
1107
1172
  };
1108
1173
  }
1109
1174
  var GHOST_TRIGGERS = ["immediate", "agent"];
@@ -1111,7 +1176,7 @@ var DEFAULT_GHOST = { linger: 4, smear: 0.3 };
1111
1176
  function normalizeGhost(raw = {}, base = DEFAULT_GHOST) {
1112
1177
  const trigger = raw.trigger === "agent" || raw.trigger === "immediate" ? raw.trigger : base.trigger;
1113
1178
  return {
1114
- linger: range(raw.linger, 0.5, 60, base.linger),
1179
+ linger: range2(raw.linger, 0.5, 60, base.linger),
1115
1180
  smear: unit2(raw.smear, base.smear),
1116
1181
  ...trigger === "agent" ? { trigger: "agent" } : {}
1117
1182
  };
@@ -1119,9 +1184,9 @@ function normalizeGhost(raw = {}, base = DEFAULT_GHOST) {
1119
1184
  var DEFAULT_MUTATION = { settle: 1.5, patience: 45, fade: 2 };
1120
1185
  function normalizeMutation(raw = {}, base = DEFAULT_MUTATION) {
1121
1186
  return {
1122
- settle: range(raw.settle, 0.5, 10, base.settle),
1123
- patience: range(raw.patience, 5, 300, base.patience),
1124
- fade: range(raw.fade, 0.5, 10, base.fade)
1187
+ settle: range2(raw.settle, 0.5, 10, base.settle),
1188
+ patience: range2(raw.patience, 5, 300, base.patience),
1189
+ fade: range2(raw.fade, 0.5, 10, base.fade)
1125
1190
  };
1126
1191
  }
1127
1192
  var DEFAULT_FERRO = { amount: 0, wavelength: 16, threshold: 0.12 };
@@ -1132,8 +1197,8 @@ function normalizeFerro(raw = {}, base = DEFAULT_FERRO) {
1132
1197
  const r = _nullishCoalesce(raw, () => ( {}));
1133
1198
  return {
1134
1199
  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)
1200
+ wavelength: range2(r.wavelength, 4, 64, base.wavelength),
1201
+ threshold: range2(r.threshold, 0.01, 0.95, base.threshold)
1137
1202
  };
1138
1203
  }
1139
1204
  function normalizeImpasto(raw = {}, base = DEFAULT_IMPASTO) {
@@ -1170,7 +1235,7 @@ var DEFAULT_DYNAMICS = {
1170
1235
  scatter: 0
1171
1236
  };
1172
1237
  var unit2 = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
1173
- var range = (v, lo, hi, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : d;
1238
+ var range2 = (v, lo, hi, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : d;
1174
1239
  function normalizeDynamics(raw = {}, base = DEFAULT_DYNAMICS) {
1175
1240
  return {
1176
1241
  minSize: unit2(raw.minSize, base.minSize),
@@ -1178,12 +1243,12 @@ function normalizeDynamics(raw = {}, base = DEFAULT_DYNAMICS) {
1178
1243
  tiltSize: unit2(raw.tiltSize, base.tiltSize),
1179
1244
  tiltRoundness: unit2(raw.tiltRoundness, base.tiltRoundness),
1180
1245
  tiltOpacity: unit2(raw.tiltOpacity, base.tiltOpacity),
1181
- velocitySize: range(raw.velocitySize, -1, 1, base.velocitySize),
1182
- velocityOpacity: range(raw.velocityOpacity, -1, 1, base.velocityOpacity),
1246
+ velocitySize: range2(raw.velocitySize, -1, 1, base.velocitySize),
1247
+ velocityOpacity: range2(raw.velocityOpacity, -1, 1, base.velocityOpacity),
1183
1248
  sizeJitter: unit2(raw.sizeJitter, base.sizeJitter),
1184
1249
  angleJitter: unit2(raw.angleJitter, base.angleJitter),
1185
1250
  opacityJitter: unit2(raw.opacityJitter, base.opacityJitter),
1186
- scatter: range(raw.scatter, 0, 4, base.scatter),
1251
+ scatter: range2(raw.scatter, 0, 4, base.scatter),
1187
1252
  ...countAndFlip(raw, base)
1188
1253
  };
1189
1254
  }
@@ -1230,9 +1295,9 @@ function readTipGrain(raw, base) {
1230
1295
  return {
1231
1296
  source,
1232
1297
  mode: TIP_GRAIN_MODES.includes(r.mode) ? r.mode : b.mode,
1233
- scale: range(r.scale, 0.05, 20, b.scale),
1298
+ scale: range2(r.scale, 0.05, 20, b.scale),
1234
1299
  depth: unit2(r.depth, b.depth),
1235
- contrast: range(r.contrast, 0, 3, b.contrast),
1300
+ contrast: range2(r.contrast, 0, 3, b.contrast),
1236
1301
  invert: typeof r.invert === "boolean" ? r.invert : b.invert
1237
1302
  };
1238
1303
  }
@@ -1248,7 +1313,7 @@ function readSpray(raw, base) {
1248
1313
  if (!raw || typeof raw !== "object") return base;
1249
1314
  const r = raw;
1250
1315
  const b = _nullishCoalesce(base, () => ( DEFAULT_SPRAY));
1251
- return { density: unit2(r.density, b.density), size: range(r.size, 0.02, 1, b.size) };
1316
+ return { density: unit2(r.density, b.density), size: range2(r.size, 0.02, 1, b.size) };
1252
1317
  }
1253
1318
  function isWetCapable(brush) {
1254
1319
  return brush.engine === "dab" || brush.engine === "mixer" || brush.engine === "wet" || brush.engine === "impasto";
@@ -1345,7 +1410,7 @@ function normalizePaintBrush(raw = {}, base) {
1345
1410
  engine,
1346
1411
  tip: _chunkIFWLZZCWcjs.normalizeBrush.call(void 0, { ...b.tip, ..._nullishCoalesce(raw.tip, () => ( {})) }),
1347
1412
  grain: unit2(raw.grain, b.grain),
1348
- pressureSmoothing: range(raw.pressureSmoothing, 0, 0.95, b.pressureSmoothing),
1413
+ pressureSmoothing: range2(raw.pressureSmoothing, 0, 0.95, b.pressureSmoothing),
1349
1414
  rotation: BRUSH_ROTATIONS.includes(raw.rotation) ? raw.rotation : b.rotation,
1350
1415
  dynamics: normalizeDynamics(_nullishCoalesce(raw.dynamics, () => ( {})), b.dynamics),
1351
1416
  mixer,
@@ -1358,6 +1423,7 @@ function normalizePaintBrush(raw = {}, base) {
1358
1423
  particle: normalizeParticle(_nullishCoalesce(raw.particle, () => ( {})), _nullishCoalesce(b.particle, () => ( DEFAULT_PARTICLE))),
1359
1424
  bristle: normalizeBristle(_nullishCoalesce(raw.bristle, () => ( {})), _nullishCoalesce(b.bristle, () => ( DEFAULT_BRISTLE))),
1360
1425
  sticker: normalizeStickerSettings(_nullishCoalesce(raw.sticker, () => ( {})), _nullishCoalesce(b.sticker, () => ( DEFAULT_STICKER))),
1426
+ antimatter: normalizeAntimatter(_nullishCoalesce(raw.antimatter, () => ( {})), _nullishCoalesce(b.antimatter, () => ( DEFAULT_ANTIMATTER))),
1361
1427
  light: ghost && !wantsLight ? { ...DEFAULT_LIGHT } : normalizeLight(_nullishCoalesce(raw.light, () => ( {})), _nullishCoalesce(b.light, () => ( DEFAULT_LIGHT))),
1362
1428
  fluorescence: normalizeFluorescence(_nullishCoalesce(raw.fluorescence, () => ( {})), _nullishCoalesce(b.fluorescence, () => ( DEFAULT_FLUORESCENCE))),
1363
1429
  thermochromic: normalizeThermochromic(_nullishCoalesce(raw.thermochromic, () => ( {})), _nullishCoalesce(b.thermochromic, () => ( DEFAULT_THERMOCHROMIC))),
@@ -1393,6 +1459,7 @@ var BASE_BRUSH = {
1393
1459
  particle: { ...DEFAULT_PARTICLE },
1394
1460
  bristle: { ...DEFAULT_BRISTLE },
1395
1461
  sticker: { ...DEFAULT_STICKER, stamps: [...DEFAULT_STICKER.stamps] },
1462
+ antimatter: { ...DEFAULT_ANTIMATTER },
1396
1463
  light: { ...DEFAULT_LIGHT },
1397
1464
  fluorescence: { ...DEFAULT_FLUORESCENCE },
1398
1465
  thermochromic: { ...DEFAULT_THERMOCHROMIC },
@@ -1403,7 +1470,7 @@ var BASE_BRUSH = {
1403
1470
  };
1404
1471
 
1405
1472
  // src/brushes/resolve.ts
1406
- var ALL = ["dab", "eraser", "mixer", "blender", "smudge", "wet", "impasto", "particle", "bristle", "sticker"];
1473
+ var ALL = ["dab", "eraser", "mixer", "blender", "smudge", "wet", "impasto", "particle", "bristle", "sticker", "antimatter"];
1407
1474
  var EXCHANGE = ["dab", "wet", "impasto"];
1408
1475
  var DABS = ["dab", "eraser", "mixer", "blender", "smudge"];
1409
1476
  var FILM = ["dab", "mixer", "wet", "impasto", "particle", "bristle"];
@@ -1497,6 +1564,7 @@ var BRUSH_FIELDS = [
1497
1564
  f("marks.size", "spray.size", ["dab"], { marks: ["scatter"] }),
1498
1565
  ...each("marks", "sticker", ["density", "sizeJitter", "rotationJitter", "scatter"], ["sticker"]),
1499
1566
  f("marks.colourJitter", "sticker.colorJitter", ["sticker"]),
1567
+ ...each("marks", "antimatter", ["blastRadius", "blastSpacing", "threshold", "invert", "invertMode", "scorch", "annihilate", "sparks", "sparkReach", "crater", "duration"], ["antimatter"]),
1500
1568
  ...each("tip.bristle", "bristle", ["count", "stiffness", "splay", "thickness", "load", "jitter"], ["bristle"])
1501
1569
  ];
1502
1570
  var WET_TOOL_OF = { water: "water", dryer: "dry", blow: "blow", blot: "blot", "wet-blend": "blend", "wet-drag": "smudge" };
@@ -1520,13 +1588,14 @@ function setPath(o, path, value) {
1520
1588
  var copy = (v) => v !== null && typeof v === "object" ? JSON.parse(JSON.stringify(v)) : v;
1521
1589
  function resolveEngine(brush) {
1522
1590
  if (brush.kind === "tool") return brush.tool === "eraser" || brush.tool === "blender" || brush.tool === "smudge" ? brush.tool : "wet";
1523
- if (_optionalChain([brush, 'access', _5 => _5.marks, 'optionalAccess', _6 => _6.kind]) === "particles") return "particle";
1524
- if (_optionalChain([brush, 'access', _7 => _7.marks, 'optionalAccess', _8 => _8.kind]) === "objects") return "sticker";
1591
+ if (_optionalChain([brush, 'access', _5 => _5.marks, 'optionalAccess', _6 => _6.kind]) === "antimatter") return "antimatter";
1592
+ if (_optionalChain([brush, 'access', _7 => _7.marks, 'optionalAccess', _8 => _8.kind]) === "particles") return "particle";
1593
+ if (_optionalChain([brush, 'access', _9 => _9.marks, 'optionalAccess', _10 => _10.kind]) === "objects") return "sticker";
1525
1594
  if (brush.tip.shape === "bristle") return "bristle";
1526
- const surface = _optionalChain([brush, 'access', _9 => _9.behaviour, 'optionalAccess', _10 => _10.surface]);
1595
+ const surface = _optionalChain([brush, 'access', _11 => _11.behaviour, 'optionalAccess', _12 => _12.surface]);
1527
1596
  if (surface === "thick") return "impasto";
1528
1597
  if (surface === "wet") return "wet";
1529
- if (_optionalChain([brush, 'access', _11 => _11.behaviour, 'optionalAccess', _12 => _12.exchange, 'optionalAccess', _13 => _13.reservoir])) return "mixer";
1598
+ if (_optionalChain([brush, 'access', _13 => _13.behaviour, 'optionalAccess', _14 => _14.exchange, 'optionalAccess', _15 => _15.reservoir])) return "mixer";
1530
1599
  return "dab";
1531
1600
  }
1532
1601
  var VARIETY_CATEGORY = {
@@ -1542,11 +1611,11 @@ var VARIETY_CATEGORY = {
1542
1611
  };
1543
1612
  var MEDIUM_CATEGORY = { paint: "paint", ink: "ink", dry: "pencil", spray: "airbrush" };
1544
1613
  function resolveWetMedium(brush) {
1545
- return _nullishCoalesce(_optionalChain([brush, 'access', _14 => _14.behaviour, 'optionalAccess', _15 => _15.wet, 'optionalAccess', _16 => _16.medium]), () => ( (WET_MEDIA.includes(brush.variety) ? brush.variety : void 0)));
1614
+ return _nullishCoalesce(_optionalChain([brush, 'access', _16 => _16.behaviour, 'optionalAccess', _17 => _17.wet, 'optionalAccess', _18 => _18.medium]), () => ( (WET_MEDIA.includes(brush.variety) ? brush.variety : void 0)));
1546
1615
  }
1547
1616
  function resolveCategory(brush, engine = resolveEngine(brush)) {
1548
1617
  if (brush.kind === "tool") return brush.tool === "eraser" || brush.tool === "blender" || brush.tool === "smudge" ? brush.tool : "wet-tool";
1549
- if ((_nullishCoalesce(_optionalChain([brush, 'access', _17 => _17.light, 'optionalAccess', _18 => _18.amount]), () => ( 0))) > 0) return "light";
1618
+ if ((_nullishCoalesce(_optionalChain([brush, 'access', _19 => _19.light, 'optionalAccess', _20 => _20.amount]), () => ( 0))) > 0) return "light";
1550
1619
  switch (engine) {
1551
1620
  case "particle":
1552
1621
  return "procedural";
@@ -1568,11 +1637,11 @@ function resolveBrush(brush, options = {}) {
1568
1637
  const engine = resolveEngine(brush);
1569
1638
  const input = { id: brush.id, name: brush.name, category: resolveCategory(brush, engine), engine };
1570
1639
  for (const field of BRUSH_FIELDS) {
1571
- if (!field.engines.includes(engine) || field.shapes && !field.shapes.includes(brush.tip.shape) || field.marks && !field.marks.includes(_nullishCoalesce(_optionalChain([brush, 'access', _19 => _19.marks, 'optionalAccess', _20 => _20.kind]), () => ( "continuous")))) continue;
1640
+ if (!field.engines.includes(engine) || field.shapes && !field.shapes.includes(brush.tip.shape) || field.marks && !field.marks.includes(_nullishCoalesce(_optionalChain([brush, 'access', _21 => _21.marks, 'optionalAccess', _22 => _22.kind]), () => ( "continuous")))) continue;
1572
1641
  const v = getPath(brush, field.facet);
1573
1642
  if (v !== void 0) setPath(input, field.engine, field.map ? field.map.toEngine(v) : copy(v));
1574
1643
  }
1575
- if (engine === "dab" && _optionalChain([brush, 'access', _21 => _21.marks, 'optionalAccess', _22 => _22.kind]) === "scatter") input.spray = { ..._nullishCoalesce(input.spray, () => ( {})) };
1644
+ if (engine === "dab" && _optionalChain([brush, 'access', _23 => _23.marks, 'optionalAccess', _24 => _24.kind]) === "scatter") input.spray = { ..._nullishCoalesce(input.spray, () => ( {})) };
1576
1645
  if (brush.iridescence && FILM.includes(engine)) input.iridescence = { ..._nullishCoalesce(input.iridescence, () => ( {})) };
1577
1646
  if (engine === "wet") {
1578
1647
  setPath(input, "wet.tool", brush.kind === "tool" ? WET_TOOL_OF[brush.tool] : "paint");
@@ -1583,7 +1652,7 @@ function resolveBrush(brush, options = {}) {
1583
1652
  }
1584
1653
  if (brush.kind !== "tool" && brush.mutation) input.mutation = { ..._nullishCoalesce(input.mutation, () => ( {})) };
1585
1654
  if (engine === "blender" || engine === "smudge") input.loadsColor = false;
1586
- const paint = _optionalChain([brush, 'access', _23 => _23.colour, 'optionalAccess', _24 => _24.default]);
1655
+ const paint = _optionalChain([brush, 'access', _25 => _25.colour, 'optionalAccess', _26 => _26.default]);
1587
1656
  if (paint && typeof paint === "object" && "type" in paint && paint.type === "gradient") input.paint = copy(paint);
1588
1657
  return normalizePaintBrush({ ...input, ...options.builtin ? { builtin: true } : {} });
1589
1658
  }
@@ -1657,6 +1726,8 @@ function skeleton(b) {
1657
1726
  return { ...brush, medium: "paint", ...mut };
1658
1727
  case "particle":
1659
1728
  return { ...brush, marks: { kind: "particles" }, ...mut };
1729
+ case "antimatter":
1730
+ return { ...brush, medium: "paint", marks: { kind: "antimatter" }, ...mut };
1660
1731
  case "sticker":
1661
1732
  return { id: b.id, name: b.name, kind: "mark", tip: { shape: tipShape(b) }, marks: { kind: "objects" }, ...mut };
1662
1733
  default: {
@@ -1671,7 +1742,7 @@ function migrateBrushV1(raw) {
1671
1742
  const brush = skeleton(b);
1672
1743
  const defaults = resolveBrush(brush);
1673
1744
  for (const field of BRUSH_FIELDS) {
1674
- if (!field.engines.includes(b.engine) || field.shapes && !field.shapes.includes(brush.tip.shape) || field.marks && !field.marks.includes(_nullishCoalesce(_optionalChain([brush, 'access', _25 => _25.marks, 'optionalAccess', _26 => _26.kind]), () => ( "continuous")))) continue;
1745
+ if (!field.engines.includes(b.engine) || field.shapes && !field.shapes.includes(brush.tip.shape) || field.marks && !field.marks.includes(_nullishCoalesce(_optionalChain([brush, 'access', _27 => _27.marks, 'optionalAccess', _28 => _28.kind]), () => ( "continuous")))) continue;
1675
1746
  const v = getPath(b, field.engine);
1676
1747
  if (!equal(v, getPath(defaults, field.engine))) setPath(brush, field.facet, field.map ? field.map.toFacet(v) : copy2(v));
1677
1748
  }
@@ -1684,7 +1755,7 @@ var BRUSH_KINDS = ["mark", "tool"];
1684
1755
  var BRUSH_MEDIA = ["paint", "ink", "dry", "spray"];
1685
1756
  var BRUSH_TOOLS = ["eraser", "blender", "smudge", "water", "dryer", "blow", "blot", "wet-blend", "wet-drag"];
1686
1757
  var BRUSH_TIP_SHAPES = ["round", "flat", "chisel", "bristle", "stamp"];
1687
- var BRUSH_MARKS_KINDS = ["continuous", "scatter", "particles", "objects"];
1758
+ var BRUSH_MARKS_KINDS = ["continuous", "scatter", "particles", "objects", "antimatter"];
1688
1759
  var BRUSH_SURFACES = ["dry", "wet", "thick"];
1689
1760
  var BRUSH_GRAIN_MODES = ["moving", "texturised"];
1690
1761
  var BRUSH_FLIPS = ["none", "x", "y", "xy"];
@@ -1811,6 +1882,24 @@ var OBJECTS = object(
1811
1882
  },
1812
1883
  ["kind"]
1813
1884
  );
1885
+ var ANTIMATTER = object(
1886
+ "Antimatter paint: where the tip touches paint across any visible layer, it detonates \u2014 inverting colours in the core, removing alpha, burning a soot ring, denting thick relief, clearing core light, and throwing sparks on the active layer. Missing fields take their defaults.",
1887
+ {
1888
+ kind: { type: "string", const: "antimatter", description: 'Always "antimatter".' },
1889
+ blastRadius: num(1, 6, "Blast size, in tip radii (default 2.5)."),
1890
+ blastSpacing: num(0.5, 8, "Stroke travel between blasts, in tip diameters (default 2)."),
1891
+ threshold: unit3("Paint needed under the tip to fire (default 0.1)."),
1892
+ invert: unit3("Strength of the colour inversion in the blast core (default 1)."),
1893
+ invertMode: oneOf2(INVERT_MODES, "Colour inversion mode: rgb (default, channel complement) or oklab (flips lightness and hue so mid-grey changes too)."),
1894
+ scorch: unit3("Strength of the soot ring around the blast (default 0.6)."),
1895
+ annihilate: unit3("Paint destroyed at the centre \u2014 alpha removed, unless the layer has lockTransparency (default 0.25)."),
1896
+ sparks: unit3("How many sparks fly out from each blast on the active layer (default 0.5; 0: none)."),
1897
+ sparkReach: num(0.5, 8, "How far sparks fly, in tip diameters (default 3)."),
1898
+ crater: unit3("Depth of the crater dent and raised rim on layers with thick relief (default 0.5)."),
1899
+ duration: num(0, 2, "Seconds a blast plays on screen (default 0.7; 0 shows the result at once).")
1900
+ },
1901
+ ["kind"]
1902
+ );
1814
1903
  var DYNAMICS = object("Pen feel: pressure, tilt, velocity, jitter, rotation and smoothing. Every amount 0 (off) .. 1 unless noted; missing fields take their defaults.", {
1815
1904
  pressureSize: bool("Pressure scales the size, down to minSize (default true)."),
1816
1905
  pressureOpacity: bool("Pressure scales the opacity (default false)."),
@@ -1981,7 +2070,7 @@ var BRUSH_JSON_SCHEMA = object(
1981
2070
  "Tools: eraser removes paint; blender mixes the colours under the tip; smudge drags paint along; water lays clear water (lifts dried paint with behaviour.wet.rewet); dryer dries wet paint; blow pushes water along; blot takes wet paint up; wet-blend evens out wet pigment; wet-drag drags wet paint."
1982
2071
  ),
1983
2072
  tip: TIP,
1984
- marks: { description: "How the tip is laid along the path (default continuous): continuous dabs, a scatter of spray droplets, particles, or objects (editable stickers).", oneOf: [CONTINUOUS, SCATTER, PARTICLES, OBJECTS] },
2073
+ marks: { description: "How the tip is laid along the path (default continuous): continuous dabs, a scatter of spray droplets, particles, objects (editable stickers), or antimatter (detonations that invert, scorch and destroy paint across every visible layer).", oneOf: [CONTINUOUS, SCATTER, PARTICLES, OBJECTS, ANTIMATTER] },
1985
2074
  dynamics: DYNAMICS,
1986
2075
  behaviour: BEHAVIOUR,
1987
2076
  light: LIGHT,
@@ -2000,7 +2089,7 @@ var BRUSH_JSON_SCHEMA = object(
2000
2089
  var BRUSH_FACET_SCHEMAS = {
2001
2090
  tip: TIP,
2002
2091
  tipGrain: TIP_GRAIN,
2003
- marks: { continuous: CONTINUOUS, scatter: SCATTER, particles: PARTICLES, objects: OBJECTS },
2092
+ marks: { continuous: CONTINUOUS, scatter: SCATTER, particles: PARTICLES, objects: OBJECTS, antimatter: ANTIMATTER },
2004
2093
  dynamics: DYNAMICS,
2005
2094
  behaviour: BEHAVIOUR,
2006
2095
  light: LIGHT,
@@ -2219,7 +2308,7 @@ function readObject(value, schema, path, problems) {
2219
2308
  const out = {};
2220
2309
  for (const [key, v] of Object.entries(value)) {
2221
2310
  if (v === void 0) continue;
2222
- const child = _optionalChain([schema, 'access', _27 => _27.properties, 'optionalAccess', _28 => _28[key]]);
2311
+ const child = _optionalChain([schema, 'access', _29 => _29.properties, 'optionalAccess', _30 => _30[key]]);
2223
2312
  if (!child) {
2224
2313
  problems.push(`${path}.${key} is not a brush field; dropped.`);
2225
2314
  continue;
@@ -2258,7 +2347,7 @@ function normalizeBrushSpec(raw, problems = []) {
2258
2347
  const { colour: rawColour, marks: rawMarks, ferro: rawFerro, kind: _k, id: _i, name: _n, tool: _t, ...rest } = raw;
2259
2348
  if (typeof rest.fluorescence === "number" && Number.isFinite(rest.fluorescence)) rest.fluorescence = { amount: rest.fluorescence };
2260
2349
  const out = readObject(rest, BRUSH_JSON_SCHEMA, "brush", problems);
2261
- if (_optionalChain([out, 'access', _29 => _29.thermochromic, 'optionalAccess', _30 => _30.hotColor])) out.thermochromic.hotColor = out.thermochromic.hotColor.toLowerCase();
2350
+ if (_optionalChain([out, 'access', _31 => _31.thermochromic, 'optionalAccess', _32 => _32.hotColor])) out.thermochromic.hotColor = out.thermochromic.hotColor.toLowerCase();
2262
2351
  const brush = { id: raw.id, name, ...out, kind, ...tool ? { tool } : {}, tip: _nullishCoalesce(out.tip, () => ( { shape: "round" })) };
2263
2352
  const ordered = () => {
2264
2353
  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;
@@ -2273,7 +2362,7 @@ function normalizeBrushSpec(raw, problems = []) {
2273
2362
  if (rawMarks !== void 0) {
2274
2363
  const k = isObject(rawMarks) ? rawMarks.kind : void 0;
2275
2364
  const schema = BRUSH_FACET_SCHEMAS.marks[k];
2276
- if (!schema) problems.push(`marks.kind ${JSON.stringify(k)} is not valid; use continuous, scatter, particles or objects. marks dropped.`);
2365
+ if (!schema) problems.push(`marks.kind ${JSON.stringify(k)} is not valid; use continuous, scatter, particles, objects or antimatter. marks dropped.`);
2277
2366
  else brush.marks = readObject(rawMarks, schema, "brush.marks", problems);
2278
2367
  }
2279
2368
  if (rawFerro !== void 0) {
@@ -2324,19 +2413,19 @@ function normalizeBrushSpec(raw, problems = []) {
2324
2413
  delete brush.iridescence;
2325
2414
  }
2326
2415
  }
2327
- if (brush.iridescence && _optionalChain([brush, 'access', _31 => _31.marks, 'optionalAccess', _32 => _32.kind]) === "objects") {
2328
- drop("iridescence", "object marks are stickers, not film");
2416
+ if (brush.iridescence && (_optionalChain([brush, 'access', _33 => _33.marks, 'optionalAccess', _34 => _34.kind]) === "objects" || _optionalChain([brush, 'access', _35 => _35.marks, 'optionalAccess', _36 => _36.kind]) === "antimatter")) {
2417
+ drop("iridescence", brush.marks.kind === "antimatter" ? "antimatter marks detonate paint, not film" : "object marks are stickers, not film");
2329
2418
  delete brush.iridescence;
2330
2419
  }
2331
- if (_optionalChain([brush, 'access', _33 => _33.marks, 'optionalAccess', _34 => _34.kind]) === "scatter") {
2332
- const surface = _optionalChain([brush, 'access', _35 => _35.behaviour, 'optionalAccess', _36 => _36.surface]);
2333
- const why = brush.tip.shape === "bristle" ? "bristle tips lay their own marks" : surface === "wet" || surface === "thick" ? `${surface} paint is not sprayed` : _optionalChain([brush, 'access', _37 => _37.behaviour, 'optionalAccess', _38 => _38.exchange, 'optionalAccess', _39 => _39.reservoir]) ? "the Mixer Brush is not sprayed" : null;
2420
+ if (_optionalChain([brush, 'access', _37 => _37.marks, 'optionalAccess', _38 => _38.kind]) === "scatter") {
2421
+ const surface = _optionalChain([brush, 'access', _39 => _39.behaviour, 'optionalAccess', _40 => _40.surface]);
2422
+ const why = brush.tip.shape === "bristle" ? "bristle tips lay their own marks" : surface === "wet" || surface === "thick" ? `${surface} paint is not sprayed` : _optionalChain([brush, 'access', _41 => _41.behaviour, 'optionalAccess', _42 => _42.exchange, 'optionalAccess', _43 => _43.reservoir]) ? "the Mixer Brush is not sprayed" : null;
2334
2423
  if (why) {
2335
2424
  drop("marks", why);
2336
2425
  delete brush.marks;
2337
2426
  }
2338
2427
  }
2339
- if (_optionalChain([brush, 'access', _40 => _40.marks, 'optionalAccess', _41 => _41.kind]) === "objects" && brush.tip.shape !== "stamp") {
2428
+ if (_optionalChain([brush, 'access', _44 => _44.marks, 'optionalAccess', _45 => _45.kind]) === "objects" && brush.tip.shape !== "stamp") {
2340
2429
  problems.push("marks objects lay stamps: tip.shape set to stamp.");
2341
2430
  brush.tip.shape = "stamp";
2342
2431
  }
@@ -2350,7 +2439,7 @@ function normalizeBrushSpec(raw, problems = []) {
2350
2439
  }
2351
2440
  if (brush.tip.stamps) {
2352
2441
  let stamps = brush.tip.stamps.map((s) => _chunkRDFHWAHLcjs.normalizeStickerRef.call(void 0, s));
2353
- if (_optionalChain([brush, 'access', _42 => _42.marks, 'optionalAccess', _43 => _43.kind]) === "objects" && stamps.some(isBrushSourceRef)) {
2442
+ if (_optionalChain([brush, 'access', _46 => _46.marks, 'optionalAccess', _47 => _47.kind]) === "objects" && stamps.some(isBrushSourceRef)) {
2354
2443
  problems.push("tip.stamps: image sources (source:\u2026) dropped: objects marks lay stickers; use continuous, scatter or particles marks for an image tip.");
2355
2444
  stamps = stamps.filter((s) => !isBrushSourceRef(s));
2356
2445
  }
@@ -2361,9 +2450,9 @@ function normalizeBrushSpec(raw, problems = []) {
2361
2450
  }
2362
2451
  }
2363
2452
  if (brush.tip.grain) {
2364
- const surface = _optionalChain([brush, 'access', _44 => _44.behaviour, 'optionalAccess', _45 => _45.surface]);
2365
- const marks = _optionalChain([brush, 'access', _46 => _46.marks, 'optionalAccess', _47 => _47.kind]);
2366
- const why = !brush.tip.grain.source ? "it needs a source (a grain pattern or source:<id>)" : brush.tip.shape === "bristle" ? "bristle tips lay their own marks" : marks === "particles" || marks === "objects" ? `${marks} marks are not dabs` : surface === "wet" || surface === "thick" ? `${surface} paint lays its own texture` : kind === "tool" && WET_BRUSH_TOOLS.includes(brush.tool) ? "the wet tools work on the wet layer" : null;
2453
+ const surface = _optionalChain([brush, 'access', _48 => _48.behaviour, 'optionalAccess', _49 => _49.surface]);
2454
+ const marks = _optionalChain([brush, 'access', _50 => _50.marks, 'optionalAccess', _51 => _51.kind]);
2455
+ const why = !brush.tip.grain.source ? "it needs a source (a grain pattern or source:<id>)" : brush.tip.shape === "bristle" ? "bristle tips lay their own marks" : marks === "particles" || marks === "objects" || marks === "antimatter" ? `${marks} marks are not dabs` : surface === "wet" || surface === "thick" ? `${surface} paint lays its own texture` : kind === "tool" && WET_BRUSH_TOOLS.includes(brush.tool) ? "the wet tools work on the wet layer" : null;
2367
2456
  if (why) {
2368
2457
  drop("tip.grain", why);
2369
2458
  delete brush.tip.grain;
@@ -2399,17 +2488,17 @@ function normalizeBrushSpec(raw, problems = []) {
2399
2488
  if (b.exchange && kind === "tool" && !wetTool) {
2400
2489
  drop("behaviour.exchange", `the ${brush.tool} does not pick paint up`);
2401
2490
  delete b.exchange;
2402
- } else if (b.exchange && (_optionalChain([brush, 'access', _48 => _48.marks, 'optionalAccess', _49 => _49.kind]) === "particles" || _optionalChain([brush, 'access', _50 => _50.marks, 'optionalAccess', _51 => _51.kind]) === "objects" || brush.tip.shape === "bristle")) {
2403
- drop("behaviour.exchange", "particle, object and bristle marks do not pick paint up");
2491
+ } else if (b.exchange && (_optionalChain([brush, 'access', _52 => _52.marks, 'optionalAccess', _53 => _53.kind]) === "particles" || _optionalChain([brush, 'access', _54 => _54.marks, 'optionalAccess', _55 => _55.kind]) === "objects" || _optionalChain([brush, 'access', _56 => _56.marks, 'optionalAccess', _57 => _57.kind]) === "antimatter" || brush.tip.shape === "bristle")) {
2492
+ drop("behaviour.exchange", "particle, object, antimatter and bristle marks do not pick paint up");
2404
2493
  delete b.exchange;
2405
2494
  }
2406
2495
  }
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")) {
2496
+ if (brush.livePatterns && (kind !== "mark" || _optionalChain([brush, 'access', _58 => _58.behaviour, 'optionalAccess', _59 => _59.surface]) !== "wet" || _optionalChain([brush, 'access', _60 => _60.marks, 'optionalAccess', _61 => _61.kind]) === "particles" || _optionalChain([brush, 'access', _62 => _62.marks, 'optionalAccess', _63 => _63.kind]) === "objects" || _optionalChain([brush, 'access', _64 => _64.marks, 'optionalAccess', _65 => _65.kind]) === "antimatter" || brush.tip.shape === "bristle")) {
2408
2497
  drop("livePatterns", "only wet paint brushes grow live patterns (set behaviour.surface to wet)");
2409
2498
  delete brush.livePatterns;
2410
2499
  }
2411
2500
  if (brush.ghost) {
2412
- if (kind === "tool" || _optionalChain([brush, 'access', _58 => _58.behaviour, 'optionalAccess', _59 => _59.surface]) !== "wet") {
2501
+ if (kind === "tool" || _optionalChain([brush, 'access', _66 => _66.behaviour, 'optionalAccess', _67 => _67.surface]) !== "wet") {
2413
2502
  drop("ghost", "it needs a mark brush with behaviour.surface wet");
2414
2503
  delete brush.ghost;
2415
2504
  } else {
@@ -2417,7 +2506,7 @@ function normalizeBrushSpec(raw, problems = []) {
2417
2506
  drop("light", "ghost paint uses the wet light channel as its tracer");
2418
2507
  delete brush.light;
2419
2508
  }
2420
- if (_optionalChain([brush, 'access', _60 => _60.behaviour, 'optionalAccess', _61 => _61.exchange])) {
2509
+ if (_optionalChain([brush, 'access', _68 => _68.behaviour, 'optionalAccess', _69 => _69.exchange])) {
2421
2510
  drop("behaviour.exchange", "ghost paint does not pick paint up");
2422
2511
  delete brush.behaviour.exchange;
2423
2512
  }
@@ -2517,30 +2606,31 @@ function checkBrush(brush, profile) {
2517
2606
  const image = brush.tip.shape === "stamp" && (_nullishCoalesce(brush.tip.stamps, () => ( []))).some(isBrushSourceRef);
2518
2607
  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)}.`);
2519
2608
  }
2520
- const marks = _nullishCoalesce(_optionalChain([brush, 'access', _62 => _62.marks, 'optionalAccess', _63 => _63.kind]), () => ( "continuous"));
2609
+ const marks = _nullishCoalesce(_optionalChain([brush, 'access', _70 => _70.marks, 'optionalAccess', _71 => _71.kind]), () => ( "continuous"));
2521
2610
  if (!profile.marks.includes(marks)) add("marks", "error", `${host} can't lay ${marks} marks.`, `Use marks ${list(profile.marks)} (remove marks for continuous dabs).`);
2522
- const surface = _nullishCoalesce(_optionalChain([brush, 'access', _64 => _64.behaviour, 'optionalAccess', _65 => _65.surface]), () => ( "dry"));
2611
+ const surface = _nullishCoalesce(_optionalChain([brush, 'access', _72 => _72.behaviour, 'optionalAccess', _73 => _73.surface]), () => ( "dry"));
2523
2612
  if (brush.kind === "mark" && !profile.surfaces.includes(surface)) add("behaviour.surface", "error", `${host} has no ${surface} surface.`, `Set behaviour.surface to ${list(profile.surfaces)}.`);
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).");
2613
+ const ex = _optionalChain([brush, 'access', _74 => _74.behaviour, 'optionalAccess', _75 => _75.exchange]);
2614
+ feature(profile.reservoir, !!_optionalChain([ex, 'optionalAccess', _76 => _76.reservoir]), "behaviour.exchange.reservoir", "a Mixer Brush reservoir", "Remove behaviour.exchange.reservoir.");
2615
+ feature(profile.exchange, !_optionalChain([ex, 'optionalAccess', _77 => _77.reservoir]) && (nonZero(_optionalChain([ex, 'optionalAccess', _78 => _78.pickup])) || !!_optionalChain([ex, 'optionalAccess', _79 => _79.dirty])), "behaviour.exchange", "picking paint up", "Remove behaviour.exchange.");
2616
+ feature(profile.grain, nonZero(_optionalChain([brush, 'access', _80 => _80.behaviour, 'optionalAccess', _81 => _81.grain])), "behaviour.grain", "paper grain", "Set behaviour.grain to 0.");
2617
+ feature(profile.light, nonZero(_optionalChain([brush, 'access', _82 => _82.light, 'optionalAccess', _83 => _83.amount])), "light", "light paint (glow and spill)", "Remove light (or set light.amount to 0).");
2529
2618
  feature(_nullishCoalesce(profile.livePatterns, () => ( "none")), Boolean(brush.livePatterns), "livePatterns", "live reaction-diffusion patterns", "Remove livePatterns.");
2530
2619
  const smartInks = profile.id === "paint" ? "full" : "none";
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.");
2620
+ feature(smartInks, nonZero(_optionalChain([brush, 'access', _84 => _84.fluorescence, 'optionalAccess', _85 => _85.amount])), "fluorescence", "UV-fluorescent ink", "Remove fluorescence (or set fluorescence.amount to 0).");
2621
+ feature(smartInks, Boolean(_optionalChain([brush, 'access', _86 => _86.thermochromic, 'optionalAccess', _87 => _87.hotColor])), "thermochromic", "thermochromic ink", "Remove thermochromic.");
2533
2622
  feature(_nullishCoalesce(profile.iridescence, () => ( "none")), !!brush.iridescence, "iridescence", "thin-film iridescence", "Remove iridescence.");
2534
2623
  feature(profile.surfaces.includes("wet") && profile.light === "full" ? "full" : "none", !!brush.ghost, "ghost", "ghost paint", "Remove ghost.");
2535
2624
  if (brush.mutation) {
2536
2625
  if (profile.id !== "paint") feature("none", true, "mutation", "mutation paint", "Remove mutation.");
2537
2626
  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.");
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.");
2627
+ else if (_optionalChain([brush, 'access', _88 => _88.marks, 'optionalAccess', _89 => _89.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.");
2628
+ else if (_optionalChain([brush, 'access', _90 => _90.marks, 'optionalAccess', _91 => _91.kind]) === "antimatter") add("mutation", "error", "Antimatter brushes detonate paint already down and cannot be mutation brushes.", "Remove marks (or use continuous marks), or remove mutation.");
2629
+ else if (_optionalChain([ex, 'optionalAccess', _92 => _92.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.");
2630
+ else if (nonZero(_optionalChain([ex, 'optionalAccess', _93 => _93.pickup])) || !!_optionalChain([ex, 'optionalAccess', _94 => _94.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.");
2631
+ else if (surface === "wet" && nonZero(_optionalChain([brush, 'access', _95 => _95.light, 'optionalAccess', _96 => _96.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.");
2542
2632
  }
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).");
2633
+ feature(_nullishCoalesce(profile.ferro, () => ( "none")), nonZero(_optionalChain([brush, 'access', _97 => _97.ferro, 'optionalAccess', _98 => _98.amount])), "ferro", "ferrofluid spikes and magnets", "Remove ferro (or set ferro.amount to 0).");
2544
2634
  const m = brush.motion;
2545
2635
  feature(profile.motion, !!m && (!!m.live || nonZero(m.waveSpeed) || m.fps !== void 0), "motion", "animation", "Remove motion.");
2546
2636
  const d = _nullishCoalesce(brush.dynamics, () => ( {}));
@@ -2549,16 +2639,16 @@ function checkBrush(brush, profile) {
2549
2639
  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.");
2550
2640
  feature(profile.tilt, nonZero(d.tiltSize) || nonZero(d.tiltRoundness) || nonZero(d.tiltOpacity), "dynamics.tilt", "tilt dynamics", "Set dynamics.tiltSize, tiltRoundness and tiltOpacity to 0.");
2551
2641
  const colour = brush.colour;
2552
- if (_optionalChain([colour, 'optionalAccess', _89 => _89.default]) !== void 0) {
2642
+ if (_optionalChain([colour, 'optionalAccess', _99 => _99.default]) !== void 0) {
2553
2643
  const kind = brushColourKind(colour.default);
2554
2644
  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.`);
2555
2645
  }
2556
- const foreign = [...new Set((_nullishCoalesce(_optionalChain([colour, 'optionalAccess', _90 => _90.palette]), () => ( []))).map(brushColourKind).filter((k) => !profile.colours.includes(k)))];
2646
+ const foreign = [...new Set((_nullishCoalesce(_optionalChain([colour, 'optionalAccess', _100 => _100.palette]), () => ( []))).map(brushColourKind).filter((k) => !profile.colours.includes(k)))];
2557
2647
  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.`);
2558
2648
  return { ok: !issues.some((i) => i.severity === "error"), issues };
2559
2649
  }
2560
2650
  function withKeptIssues(check, before) {
2561
- const had = new Set((_nullishCoalesce(_optionalChain([before, 'optionalAccess', _91 => _91.issues]), () => ( []))).filter((i) => i.severity === "error").map((i) => i.facet));
2651
+ const had = new Set((_nullishCoalesce(_optionalChain([before, 'optionalAccess', _101 => _101.issues]), () => ( []))).filter((i) => i.severity === "error").map((i) => i.facet));
2562
2652
  const kept = check.issues.filter((i) => i.severity === "error" && had.has(i.facet));
2563
2653
  const issues = check.issues.filter((i) => !kept.includes(i));
2564
2654
  return { ok: !issues.some((i) => i.severity === "error"), issues, kept };
@@ -2657,7 +2747,7 @@ function degrade(brush, profile, options = {}) {
2657
2747
  }
2658
2748
  dropped.push(issue);
2659
2749
  }
2660
- if (_optionalChain([b, 'access', _92 => _92.behaviour, 'optionalAccess', _93 => _93.exchange]) && !Object.keys(b.behaviour.exchange).length) delete b.behaviour.exchange;
2750
+ if (_optionalChain([b, 'access', _102 => _102.behaviour, 'optionalAccess', _103 => _103.exchange]) && !Object.keys(b.behaviour.exchange).length) delete b.behaviour.exchange;
2661
2751
  if (b.colour && !Object.keys(b.colour).length) delete b.colour;
2662
2752
  const after = checkBrush(b, profile).issues.filter((i) => i.severity === "error");
2663
2753
  return { brush: b, dropped, issues: after };
@@ -2701,7 +2791,7 @@ function parseBrushLibrary2(raw, problems = []) {
2701
2791
  if (file && file.version !== void 0 && file.version !== 1 && file.version !== 2) {
2702
2792
  throw new BrushLibraryError("invalid", `Brush library version ${JSON.stringify(file.version)} is not supported; this library reads versions 1 and 2.`);
2703
2793
  }
2704
- const v1File = _optionalChain([file, 'optionalAccess', _94 => _94.version]) === 1;
2794
+ const v1File = _optionalChain([file, 'optionalAccess', _104 => _104.version]) === 1;
2705
2795
  const out = [];
2706
2796
  items.forEach((item, i) => {
2707
2797
  const at = `brushes[${i}]`;
@@ -2962,7 +3052,7 @@ function snapshotBrushLibrary(library, onChange) {
2962
3052
  (list2) => {
2963
3053
  entries = list2;
2964
3054
  user = list2.filter((e) => !e.builtin).map((e) => e.brush);
2965
- _optionalChain([onChange, 'optionalCall', _95 => _95()]);
3055
+ _optionalChain([onChange, 'optionalCall', _105 => _105()]);
2966
3056
  },
2967
3057
  () => void 0
2968
3058
  );
@@ -3106,4 +3196,7 @@ function snapshotBrushLibrary(library, onChange) {
3106
3196
 
3107
3197
 
3108
3198
 
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;
3199
+
3200
+
3201
+
3202
+ 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.INVERT_MODES = INVERT_MODES; exports.DEFAULT_ANTIMATTER = DEFAULT_ANTIMATTER; exports.normalizeAntimatter = normalizeAntimatter; 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;