@pixerate/editor 0.274.0 → 0.275.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-editor/index.cjs +60 -60
- package/dist/animation-editor/index.js +4 -4
- package/dist/brushes/index.cjs +102 -74
- package/dist/brushes/index.js +34 -6
- package/dist/brushes/patch.d.cts +1 -1
- package/dist/brushes/patch.d.ts +1 -1
- package/dist/brushes/preview.d.cts +3 -0
- package/dist/brushes/preview.d.ts +3 -0
- package/dist/brushes/profiles.d.cts +2 -0
- package/dist/brushes/profiles.d.ts +2 -0
- package/dist/brushes/schema.d.cts +4 -0
- package/dist/brushes/schema.d.ts +4 -0
- package/dist/brushes/types.d.cts +47 -0
- package/dist/brushes/types.d.ts +47 -0
- package/dist/{brushstrokeRender-5YBCLFDQ.cjs → brushstrokeRender-2RQL46WB.cjs} +15 -15
- package/dist/{brushstrokeRender-DZGFU3J4.js → brushstrokeRender-WGY4DZHF.js} +3 -3
- package/dist/canvas/index.cjs +13 -13
- package/dist/canvas/index.js +1 -1
- package/dist/chart/contract.d.cts +3 -1
- package/dist/chart/contract.d.ts +3 -1
- package/dist/{chunk-2XDNF73E.js → chunk-43XJC3PP.js} +1 -1
- package/dist/{chunk-GHGBIAWQ.cjs → chunk-6QNDGMK7.cjs} +263 -45
- package/dist/{chunk-2F3YDXCN.js → chunk-75SYKUOT.js} +1 -1
- package/dist/{chunk-M3RZOF3G.cjs → chunk-C6VB7CPY.cjs} +1 -1
- package/dist/{chunk-N5IMDAHM.cjs → chunk-DHC4E2DU.cjs} +89 -37
- package/dist/{chunk-NAHJV3O6.js → chunk-G5TCMKX5.js} +28 -11
- package/dist/{chunk-DZVRH63Y.js → chunk-IEEY5RJV.js} +1 -1
- package/dist/{chunk-7W2EQQBP.cjs → chunk-JDXWLCXF.cjs} +6 -6
- package/dist/{chunk-N3SKPQEH.js → chunk-LMG57L5A.js} +62 -10
- package/dist/{chunk-5HP2VSAS.js → chunk-MSWLJI4G.js} +1 -1
- package/dist/{chunk-SNAFFCAX.cjs → chunk-OLZXXAER.cjs} +221 -196
- package/dist/{chunk-YJANF2BL.cjs → chunk-RAPNRK4R.cjs} +86 -86
- package/dist/{chunk-CLB2I2FU.js → chunk-RJGW3RQS.js} +2 -2
- package/dist/{chunk-FXVCB6YG.cjs → chunk-T6KNJVBP.cjs} +35 -35
- package/dist/{chunk-ZEI7ZEA4.js → chunk-Y2ISSVPH.js} +34 -9
- package/dist/{chunk-3JKCREEU.cjs → chunk-Y354ETAE.cjs} +76 -59
- package/dist/{chunk-TNLKFW7D.cjs → chunk-YYZC2EQO.cjs} +23 -23
- package/dist/{chunk-UXYCY3HP.js → chunk-ZM7IKQ4X.js} +239 -21
- package/dist/image-editor/index.cjs +789 -789
- package/dist/image-editor/index.js +5 -5
- package/dist/paint/index.cjs +2 -2
- package/dist/paint/index.js +1 -1
- package/dist/paint/wet/world.d.cts +15 -2
- package/dist/paint/wet/world.d.ts +15 -2
- package/dist/paint-editor/brush-controls.d.cts +7 -0
- package/dist/paint-editor/brush-controls.d.ts +7 -0
- package/dist/paint-editor/brush-model.d.cts +59 -1
- package/dist/paint-editor/brush-model.d.ts +59 -1
- package/dist/paint-editor/controller.d.cts +168 -1
- package/dist/paint-editor/controller.d.ts +168 -1
- package/dist/paint-editor/dab-stroke.d.cts +3 -0
- package/dist/paint-editor/dab-stroke.d.ts +3 -0
- package/dist/paint-editor/document.d.cts +13 -3
- package/dist/paint-editor/document.d.ts +13 -3
- package/dist/paint-editor/engine.d.cts +3 -0
- package/dist/paint-editor/engine.d.ts +3 -0
- package/dist/paint-editor/heat.d.cts +156 -0
- package/dist/paint-editor/heat.d.ts +156 -0
- package/dist/paint-editor/index.cjs +3386 -855
- package/dist/paint-editor/index.d.cts +3 -0
- package/dist/paint-editor/index.d.ts +3 -0
- package/dist/paint-editor/index.js +2731 -200
- package/dist/paint-editor/mutation-tools.d.cts +13 -0
- package/dist/paint-editor/mutation-tools.d.ts +13 -0
- package/dist/paint-editor/mutation.d.cts +67 -0
- package/dist/paint-editor/mutation.d.ts +67 -0
- package/dist/paint-editor/presence.d.cts +7 -1
- package/dist/paint-editor/presence.d.ts +7 -1
- package/dist/paint-editor/procedural.d.cts +3 -0
- package/dist/paint-editor/procedural.d.ts +3 -0
- package/dist/paint-editor/stroke.d.cts +32 -2
- package/dist/paint-editor/stroke.d.ts +32 -2
- package/dist/paint-editor/thick.d.cts +48 -7
- package/dist/paint-editor/thick.d.ts +48 -7
- package/dist/paint-editor/ui.d.cts +34 -1
- package/dist/paint-editor/ui.d.ts +34 -1
- package/dist/paint-editor/wet.d.cts +8 -1
- package/dist/paint-editor/wet.d.ts +8 -1
- package/dist/vector/index.cjs +999 -999
- package/dist/vector/index.js +5 -5
- package/package.json +1 -1
- package/skills/pixerate-brushes/SKILL.md +1 -0
- package/skills/pixerate-paint-editor/SKILL.md +7 -1
- package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
- package/skills/pixerate-paint-editor/references/live-patterns.md +41 -0
- package/skills/pixerate-paint-watercolour/SKILL.md +8 -0
|
@@ -776,6 +776,69 @@ var BRUSH_PRESETS = [
|
|
|
776
776
|
dynamics: { smoothing: 0.2, pressureSize: false, pressureOpacity: true },
|
|
777
777
|
colour: { palette: ["#ef4444", "#f97316", "#facc15", "#22c55e", "#3b82f6", "#a855f7", "#111827", "#f8fafc"] }
|
|
778
778
|
},
|
|
779
|
+
// --- Smart inks: UV-fluorescent and thermochromic ------------------------------------
|
|
780
|
+
// Invisible UV marker: invisible in normal light, glows brightly under the UV blacklight.
|
|
781
|
+
{
|
|
782
|
+
id: "uv-marker",
|
|
783
|
+
name: "UV marker",
|
|
784
|
+
kind: "mark",
|
|
785
|
+
medium: "ink",
|
|
786
|
+
variety: "uv-ink",
|
|
787
|
+
tip: { shape: "round", size: 18, hardness: 0.75, spacing: 0.06, opacity: 0.9 },
|
|
788
|
+
dynamics: { smoothing: 0.3, minSize: 0.5 },
|
|
789
|
+
fluorescence: { amount: 0.9, invisible: true },
|
|
790
|
+
colour: { default: "#22d3ee", palette: ["#22d3ee", "#a855f7", "#4ade80", "#f472b6"] }
|
|
791
|
+
},
|
|
792
|
+
// Thermochromic ink: shifts smoothly toward its hot colour where the canvas heat field warms it.
|
|
793
|
+
{
|
|
794
|
+
id: "thermo-ink",
|
|
795
|
+
name: "Thermochromic ink",
|
|
796
|
+
kind: "mark",
|
|
797
|
+
medium: "ink",
|
|
798
|
+
variety: "thermochromic",
|
|
799
|
+
tip: { shape: "round", size: 12, hardness: 0.85, spacing: 0.05 },
|
|
800
|
+
dynamics: { smoothing: 0.3, minSize: 0.4 },
|
|
801
|
+
thermochromic: { hotColor: "#ff7a18", threshold: 0.45 },
|
|
802
|
+
colour: { default: "#1e3a8a", palette: ["#1e3a8a", "#312e81", "#065f46", "#7f1d1d"] }
|
|
803
|
+
},
|
|
804
|
+
// --- Structural colour (thin-film iridescence) ---------------------------------------
|
|
805
|
+
// Scarab lacquer: thick metallic impasto under a high-index chitin-like thin film.
|
|
806
|
+
{
|
|
807
|
+
id: "scarab-lacquer",
|
|
808
|
+
name: "Scarab lacquer",
|
|
809
|
+
kind: "mark",
|
|
810
|
+
medium: "paint",
|
|
811
|
+
variety: "iridescent",
|
|
812
|
+
tip: { shape: "round", size: 22, hardness: 0.75, spacing: 0.06 },
|
|
813
|
+
dynamics: { smoothing: 0.25, minSize: 0.3 },
|
|
814
|
+
behaviour: { surface: "thick", thick: { thickness: 0.7, gloss: 0.85, metallic: 0.5 } },
|
|
815
|
+
iridescence: { thickness: 480, pressureToThickness: 0.35, ior: 1.65 },
|
|
816
|
+
colour: { default: "#065f46", palette: ["#065f46", "#1e3a8a", "#581c87", "#7c2d12", "#111827"] }
|
|
817
|
+
},
|
|
818
|
+
// Soap film: a soft glaze whose thin watery film shifts across the spectrum with pressure.
|
|
819
|
+
{
|
|
820
|
+
id: "soap-film",
|
|
821
|
+
name: "Soap film",
|
|
822
|
+
kind: "mark",
|
|
823
|
+
medium: "paint",
|
|
824
|
+
variety: "iridescent",
|
|
825
|
+
tip: { shape: "round", size: 32, hardness: 0.4, spacing: 0.06, opacity: 0.85, flow: 0.75 },
|
|
826
|
+
dynamics: { smoothing: 0.35, pressureOpacity: true, minSize: 0.35 },
|
|
827
|
+
iridescence: { thickness: 340, pressureToThickness: 0.8, ior: 1.33 },
|
|
828
|
+
colour: { default: { light: "#0284c7", dark: "#bae6fd" }, palette: [{ light: "#0284c7", dark: "#bae6fd" }, "#f472b6", "#a78bfa", "#34d399"] }
|
|
829
|
+
},
|
|
830
|
+
// Pearl glaze: a flat nacre glaze with gentle interference colour shifts.
|
|
831
|
+
{
|
|
832
|
+
id: "pearl-glaze",
|
|
833
|
+
name: "Pearl glaze",
|
|
834
|
+
kind: "mark",
|
|
835
|
+
medium: "paint",
|
|
836
|
+
variety: "iridescent",
|
|
837
|
+
tip: { shape: "flat", size: 28, hardness: 0.5, spacing: 0.06, angle: 25, roundness: 0.4, opacity: 0.9, flow: 0.85 },
|
|
838
|
+
dynamics: { smoothing: 0.3, rotation: "stroke", minSize: 0.4 },
|
|
839
|
+
iridescence: { thickness: 560, pressureToThickness: 0.25, ior: 1.55 },
|
|
840
|
+
colour: { default: { light: "#475569", dark: "#f1f5f9" }, palette: [{ light: "#475569", dark: "#f1f5f9" }, "#fbcfe8", "#ddd6fe", "#bae6fd", "#fef3c7"] }
|
|
841
|
+
},
|
|
779
842
|
// --- Ghost paint: wet paint whose colour vanishes, leaving only what it smeared ---
|
|
780
843
|
{
|
|
781
844
|
id: "ghost-wash",
|
|
@@ -820,6 +883,28 @@ var BRUSH_PRESETS = [
|
|
|
820
883
|
dynamics: { smoothing: 0.3, minSize: 0.4 },
|
|
821
884
|
behaviour: { surface: "wet", wet: { water: 0.45, pigment: 0, rewet: 0.35 } },
|
|
822
885
|
ghost: { smear: 0.65 }
|
|
886
|
+
},
|
|
887
|
+
// --- Mutation paint: strokes that sink away once claimed, and an agent answers ---
|
|
888
|
+
{
|
|
889
|
+
id: "mutation-wash",
|
|
890
|
+
name: "Mutation wash",
|
|
891
|
+
kind: "mark",
|
|
892
|
+
medium: "paint",
|
|
893
|
+
variety: "watercolor",
|
|
894
|
+
tip: { shape: "round", size: 28, hardness: 0.6, spacing: 0.12 },
|
|
895
|
+
dynamics: { smoothing: 0.25, pressureOpacity: true, minSize: 0.35 },
|
|
896
|
+
behaviour: { surface: "wet", wet: { water: 0.6, rewet: 0.2 } },
|
|
897
|
+
mutation: {}
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
id: "mutation-ink",
|
|
901
|
+
name: "Mutation ink",
|
|
902
|
+
kind: "mark",
|
|
903
|
+
medium: "ink",
|
|
904
|
+
variety: "pen",
|
|
905
|
+
tip: { shape: "round", size: 6, hardness: 0.9, spacing: 0.06 },
|
|
906
|
+
dynamics: { smoothing: 0.35, pressureSmoothing: 0.25, minSize: 0.25 },
|
|
907
|
+
mutation: {}
|
|
823
908
|
}
|
|
824
909
|
];
|
|
825
910
|
|
|
@@ -942,11 +1027,51 @@ var DEFAULT_LIGHT = { amount: 0, pressure: false };
|
|
|
942
1027
|
function normalizeLight(raw = {}, base = DEFAULT_LIGHT) {
|
|
943
1028
|
return { amount: unit2(raw.amount, base.amount), pressure: typeof raw.pressure === "boolean" ? raw.pressure : base.pressure };
|
|
944
1029
|
}
|
|
1030
|
+
var DEFAULT_FLUORESCENCE = { amount: 0, invisible: false };
|
|
1031
|
+
function normalizeFluorescence(raw = {}, base = DEFAULT_FLUORESCENCE) {
|
|
1032
|
+
if (typeof raw === "number") return { amount: unit2(raw, base.amount), invisible: base.invisible };
|
|
1033
|
+
return {
|
|
1034
|
+
amount: unit2(raw.amount, base.amount),
|
|
1035
|
+
invisible: typeof raw.invisible === "boolean" ? raw.invisible : base.invisible
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
var DEFAULT_THERMOCHROMIC = { hotColor: null, threshold: 0.5 };
|
|
1039
|
+
function normalizeThermochromic(raw = {}, base = DEFAULT_THERMOCHROMIC) {
|
|
1040
|
+
const hotColor = raw.hotColor === null ? null : typeof raw.hotColor === "string" && HEX.test(raw.hotColor.trim()) ? raw.hotColor.trim().toLowerCase() : base.hotColor;
|
|
1041
|
+
return {
|
|
1042
|
+
hotColor,
|
|
1043
|
+
threshold: range(raw.threshold, 0.05, 0.95, base.threshold)
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
var DEFAULT_IRIDESCENCE = { thickness: 400, pressureToThickness: 0, ior: 1.5 };
|
|
1047
|
+
var withIridescence = (ir) => ir ? { iridescence: ir } : {};
|
|
1048
|
+
function readIridescence(raw, base) {
|
|
1049
|
+
if (raw === null) return null;
|
|
1050
|
+
if (!raw || typeof raw !== "object") return base;
|
|
1051
|
+
const r = raw;
|
|
1052
|
+
const b = _nullishCoalesce(base, () => ( DEFAULT_IRIDESCENCE));
|
|
1053
|
+
return {
|
|
1054
|
+
thickness: range(r.thickness, 100, 1e3, b.thickness),
|
|
1055
|
+
pressureToThickness: unit2(r.pressureToThickness, b.pressureToThickness),
|
|
1056
|
+
ior: range(r.ior, 1.2, 2, b.ior)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
var GHOST_TRIGGERS = ["immediate", "agent"];
|
|
945
1060
|
var DEFAULT_GHOST = { linger: 4, smear: 0.3 };
|
|
946
1061
|
function normalizeGhost(raw = {}, base = DEFAULT_GHOST) {
|
|
1062
|
+
const trigger = raw.trigger === "agent" || raw.trigger === "immediate" ? raw.trigger : base.trigger;
|
|
947
1063
|
return {
|
|
948
1064
|
linger: range(raw.linger, 0.5, 60, base.linger),
|
|
949
|
-
smear: unit2(raw.smear, base.smear)
|
|
1065
|
+
smear: unit2(raw.smear, base.smear),
|
|
1066
|
+
...trigger === "agent" ? { trigger: "agent" } : {}
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
var DEFAULT_MUTATION = { settle: 1.5, patience: 45, fade: 2 };
|
|
1070
|
+
function normalizeMutation(raw = {}, base = DEFAULT_MUTATION) {
|
|
1071
|
+
return {
|
|
1072
|
+
settle: range(raw.settle, 0.5, 10, base.settle),
|
|
1073
|
+
patience: range(raw.patience, 5, 300, base.patience),
|
|
1074
|
+
fade: range(raw.fade, 0.5, 10, base.fade)
|
|
950
1075
|
};
|
|
951
1076
|
}
|
|
952
1077
|
function normalizeImpasto(raw = {}, base = DEFAULT_IMPASTO) {
|
|
@@ -1139,8 +1264,12 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1139
1264
|
behavior = { ...behavior, multicolor: _nullishCoalesce(paintToMultiColor(paint), () => ( { ...DEFAULT_MULTI_COLOR, colors: [] })) };
|
|
1140
1265
|
const wet = normalizeWetBrush(_nullishCoalesce(raw.wet, () => ( {})), _nullishCoalesce(b.wet, () => ( DEFAULT_WET_BRUSH)));
|
|
1141
1266
|
const wantsLight = typeof _optionalChain([raw, 'access', _3 => _3.light, 'optionalAccess', _4 => _4.amount]) === "number" && raw.light.amount > 0;
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1267
|
+
const canMutate = engine === "dab" || engine === "wet" && wet.tool === "paint" || engine === "impasto" || engine === "particle" || engine === "bristle";
|
|
1268
|
+
const mutation = canMutate && (raw.mutation !== void 0 ? raw.mutation !== null : Boolean(b.mutation)) ? normalizeMutation(_nullishCoalesce(raw.mutation, () => ( {})), _nullishCoalesce(b.mutation, () => ( DEFAULT_MUTATION))) : void 0;
|
|
1269
|
+
const hadAutoGhost = Boolean(b.mutation && raw.mutation === null && raw.ghost === void 0);
|
|
1270
|
+
const ghostBase = mutation ? { ..._nullishCoalesce(b.ghost, () => ( DEFAULT_GHOST)), linger: mutation.fade, trigger: "agent" } : _nullishCoalesce(b.ghost, () => ( DEFAULT_GHOST));
|
|
1271
|
+
const ghost = engine === "wet" && wet.tool === "paint" && !hadAutoGhost && (raw.ghost !== void 0 ? raw.ghost !== null : Boolean(mutation) || !wantsLight && Boolean(b.ghost)) ? normalizeGhost(_nullishCoalesce(raw.ghost, () => ( {})), ghostBase) : void 0;
|
|
1272
|
+
if (ghost || mutation) behavior = { ...behavior, pickup: 0, dirty: false };
|
|
1144
1273
|
return {
|
|
1145
1274
|
id,
|
|
1146
1275
|
name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim().slice(0, 80) : b.name,
|
|
@@ -1162,7 +1291,11 @@ function normalizePaintBrush(raw = {}, base) {
|
|
|
1162
1291
|
bristle: normalizeBristle(_nullishCoalesce(raw.bristle, () => ( {})), _nullishCoalesce(b.bristle, () => ( DEFAULT_BRISTLE))),
|
|
1163
1292
|
sticker: normalizeStickerSettings(_nullishCoalesce(raw.sticker, () => ( {})), _nullishCoalesce(b.sticker, () => ( DEFAULT_STICKER))),
|
|
1164
1293
|
light: ghost && !wantsLight ? { ...DEFAULT_LIGHT } : normalizeLight(_nullishCoalesce(raw.light, () => ( {})), _nullishCoalesce(b.light, () => ( DEFAULT_LIGHT))),
|
|
1294
|
+
fluorescence: normalizeFluorescence(_nullishCoalesce(raw.fluorescence, () => ( {})), _nullishCoalesce(b.fluorescence, () => ( DEFAULT_FLUORESCENCE))),
|
|
1295
|
+
thermochromic: normalizeThermochromic(_nullishCoalesce(raw.thermochromic, () => ( {})), _nullishCoalesce(b.thermochromic, () => ( DEFAULT_THERMOCHROMIC))),
|
|
1296
|
+
...withIridescence(readIridescence(raw.iridescence, _nullishCoalesce(b.iridescence, () => ( null)))),
|
|
1165
1297
|
...ghost ? { ghost } : {},
|
|
1298
|
+
...mutation ? { mutation } : {},
|
|
1166
1299
|
stamp: readStamp(raw.stamp, _nullishCoalesce(b.stamp, () => ( null))),
|
|
1167
1300
|
spray: readSpray(raw.spray, _nullishCoalesce(b.spray, () => ( null))),
|
|
1168
1301
|
...withTipGrain(readTipGrain(raw.tipGrain, _nullishCoalesce(b.tipGrain, () => ( null)))),
|
|
@@ -1191,6 +1324,8 @@ var BASE_BRUSH = {
|
|
|
1191
1324
|
bristle: { ...DEFAULT_BRISTLE },
|
|
1192
1325
|
sticker: { ...DEFAULT_STICKER, stamps: [...DEFAULT_STICKER.stamps] },
|
|
1193
1326
|
light: { ...DEFAULT_LIGHT },
|
|
1327
|
+
fluorescence: { ...DEFAULT_FLUORESCENCE },
|
|
1328
|
+
thermochromic: { ...DEFAULT_THERMOCHROMIC },
|
|
1194
1329
|
stamp: null,
|
|
1195
1330
|
spray: null,
|
|
1196
1331
|
paint: null
|
|
@@ -1200,6 +1335,7 @@ var BASE_BRUSH = {
|
|
|
1200
1335
|
var ALL = ["dab", "eraser", "mixer", "blender", "smudge", "wet", "impasto", "particle", "bristle", "sticker"];
|
|
1201
1336
|
var EXCHANGE = ["dab", "wet", "impasto"];
|
|
1202
1337
|
var DABS = ["dab", "eraser", "mixer", "blender", "smudge"];
|
|
1338
|
+
var FILM = ["dab", "mixer", "wet", "impasto", "particle", "bristle"];
|
|
1203
1339
|
var NOT = { toEngine: (v) => !v, toFacet: (v) => !v };
|
|
1204
1340
|
var EDITOR_STAMPS = {
|
|
1205
1341
|
toEngine: (v) => v.flatMap((ref) => {
|
|
@@ -1229,6 +1365,9 @@ var BRUSH_FIELDS = [
|
|
|
1229
1365
|
f("tip.grain", "tipGrain", DABS),
|
|
1230
1366
|
f("behaviour.grain", "grain"),
|
|
1231
1367
|
...each("light", "light", ["amount", "pressure"]),
|
|
1368
|
+
...each("fluorescence", "fluorescence", ["amount", "invisible"]),
|
|
1369
|
+
...each("thermochromic", "thermochromic", ["hotColor", "threshold"]),
|
|
1370
|
+
...each("iridescence", "iridescence", ["thickness", "pressureToThickness", "ior"], FILM),
|
|
1232
1371
|
// Paint exchange: the dab, wet and impasto engines' behaviour, or the Mixer Brush's settings.
|
|
1233
1372
|
f("behaviour.exchange.pickup", "behavior.pickup", EXCHANGE),
|
|
1234
1373
|
f("behaviour.exchange.mix", "behavior.mix", EXCHANGE),
|
|
@@ -1251,7 +1390,8 @@ var BRUSH_FIELDS = [
|
|
|
1251
1390
|
f("behaviour.strength", "wet.strength", ["wet"]),
|
|
1252
1391
|
// Surfaces.
|
|
1253
1392
|
...each("behaviour.wet", "wet", ["medium", "water", "pigment", "body", "rewet", "load", "thinner"], ["wet"]),
|
|
1254
|
-
...each("ghost", "ghost", ["linger", "smear"], ["wet"]),
|
|
1393
|
+
...each("ghost", "ghost", ["linger", "smear", "trigger"], ["wet"]),
|
|
1394
|
+
...each("mutation", "mutation", ["settle", "patience", "fade"], ["dab", "wet", "impasto", "particle", "bristle"]),
|
|
1255
1395
|
f("behaviour.thick.applicator", "impasto.tool", ["impasto"]),
|
|
1256
1396
|
...each("behaviour.thick", "impasto", ["thickness", "gloss", "metallic", "load", "thinner", "drag", "rim", "clarity"], ["impasto"]),
|
|
1257
1397
|
// Marks.
|
|
@@ -1343,12 +1483,14 @@ function resolveBrush(brush, options = {}) {
|
|
|
1343
1483
|
if (v !== void 0) setPath(input, field.engine, field.map ? field.map.toEngine(v) : copy(v));
|
|
1344
1484
|
}
|
|
1345
1485
|
if (engine === "dab" && _optionalChain([brush, 'access', _21 => _21.marks, 'optionalAccess', _22 => _22.kind]) === "scatter") input.spray = { ..._nullishCoalesce(input.spray, () => ( {})) };
|
|
1486
|
+
if (brush.iridescence && FILM.includes(engine)) input.iridescence = { ..._nullishCoalesce(input.iridescence, () => ( {})) };
|
|
1346
1487
|
if (engine === "wet") {
|
|
1347
1488
|
setPath(input, "wet.tool", brush.kind === "tool" ? WET_TOOL_OF[brush.tool] : "paint");
|
|
1348
1489
|
const medium = resolveWetMedium(brush);
|
|
1349
1490
|
if (medium) setPath(input, "wet.medium", medium);
|
|
1350
1491
|
if (brush.kind !== "tool" && brush.ghost) input.ghost = { ..._nullishCoalesce(input.ghost, () => ( {})) };
|
|
1351
1492
|
}
|
|
1493
|
+
if (brush.kind !== "tool" && brush.mutation) input.mutation = { ..._nullishCoalesce(input.mutation, () => ( {})) };
|
|
1352
1494
|
if (engine === "blender" || engine === "smudge") input.loadsColor = false;
|
|
1353
1495
|
const paint = _optionalChain([brush, 'access', _23 => _23.colour, 'optionalAccess', _24 => _24.default]);
|
|
1354
1496
|
if (paint && typeof paint === "object" && "type" in paint && paint.type === "gradient") input.paint = copy(paint);
|
|
@@ -1405,32 +1547,34 @@ function tipShape(b) {
|
|
|
1405
1547
|
return Math.abs(Math.abs(b.tip.angle % 180) - 45) < 1e-9 || Math.abs(Math.abs(b.tip.angle % 180) - 135) < 1e-9 ? "chisel" : "flat";
|
|
1406
1548
|
}
|
|
1407
1549
|
function skeleton(b) {
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1550
|
+
const irid = b.iridescence ? { iridescence: {} } : {};
|
|
1551
|
+
const brush = { id: b.id, name: b.name, kind: "mark", tip: { shape: tipShape(b) }, ...irid };
|
|
1552
|
+
if (b.engine === "eraser" || b.engine === "blender" || b.engine === "smudge") return { id: b.id, name: b.name, kind: "tool", tool: b.engine, tip: { shape: tipShape(b) } };
|
|
1410
1553
|
if (b.engine === "wet" && b.wet.tool !== "paint") {
|
|
1411
1554
|
const tool = Object.keys(WET_TOOL_OF).find((t) => WET_TOOL_OF[t] === b.wet.tool);
|
|
1412
|
-
return {
|
|
1555
|
+
return { id: b.id, name: b.name, kind: "tool", tool, tip: { shape: tipShape(b) } };
|
|
1413
1556
|
}
|
|
1557
|
+
const mut = b.mutation ? { mutation: {} } : {};
|
|
1414
1558
|
switch (b.engine) {
|
|
1415
1559
|
case "wet":
|
|
1416
|
-
return { ...brush, medium: "paint", variety: b.wet.medium, behaviour: { surface: "wet" }, ...b.ghost ? { ghost: {} } : {} };
|
|
1560
|
+
return { ...brush, medium: "paint", variety: b.wet.medium, behaviour: { surface: "wet" }, ...b.ghost && !b.mutation ? { ghost: {} } : {}, ...mut };
|
|
1417
1561
|
case "impasto":
|
|
1418
|
-
return { ...brush, medium: "paint", behaviour: { surface: "thick" } };
|
|
1562
|
+
return { ...brush, medium: "paint", behaviour: { surface: "thick" }, ...mut };
|
|
1419
1563
|
case "mixer":
|
|
1420
|
-
return { ...brush, medium: "paint", variety: "mixer", behaviour: { exchange: { reservoir: {} } } };
|
|
1564
|
+
return { ...brush, medium: "paint", variety: "mixer", behaviour: { exchange: { reservoir: {} } }, ...mut };
|
|
1421
1565
|
case "bristle":
|
|
1422
|
-
return { ...brush, medium: "paint" };
|
|
1566
|
+
return { ...brush, medium: "paint", ...mut };
|
|
1423
1567
|
case "particle":
|
|
1424
|
-
return { ...brush, marks: { kind: "particles" } };
|
|
1568
|
+
return { ...brush, marks: { kind: "particles" }, ...mut };
|
|
1425
1569
|
case "sticker":
|
|
1426
|
-
return {
|
|
1570
|
+
return { id: b.id, name: b.name, kind: "mark", tip: { shape: tipShape(b) }, marks: { kind: "objects" }, ...mut };
|
|
1427
1571
|
default: {
|
|
1428
1572
|
const [medium, variety] = _nullishCoalesce(CATEGORY_MEDIUM[b.category], () => ( []));
|
|
1429
|
-
return { ...brush, ...medium ? { medium } : {}, ...variety ? { variety } : {}, ...b.engine === "dab" && b.spray ? { marks: { kind: "scatter" } } : {} };
|
|
1573
|
+
return { ...brush, ...medium ? { medium } : {}, ...variety ? { variety } : {}, ...b.engine === "dab" && b.spray ? { marks: { kind: "scatter" } } : {}, ...mut };
|
|
1430
1574
|
}
|
|
1431
1575
|
}
|
|
1432
1576
|
}
|
|
1433
|
-
var ORDER = ["id", "name", "description", "tags", "kind", "medium", "variety", "tool", "tip", "marks", "dynamics", "behaviour", "light", "ghost", "motion", "colour"];
|
|
1577
|
+
var ORDER = ["id", "name", "description", "tags", "kind", "medium", "variety", "tool", "tip", "marks", "dynamics", "behaviour", "light", "fluorescence", "thermochromic", "iridescence", "ghost", "mutation", "motion", "colour"];
|
|
1434
1578
|
function migrateBrushV1(raw) {
|
|
1435
1579
|
const b = normalizePaintBrush(raw);
|
|
1436
1580
|
const brush = skeleton(b);
|
|
@@ -1453,6 +1597,7 @@ var BRUSH_MARKS_KINDS = ["continuous", "scatter", "particles", "objects"];
|
|
|
1453
1597
|
var BRUSH_SURFACES = ["dry", "wet", "thick"];
|
|
1454
1598
|
var BRUSH_GRAIN_MODES = ["moving", "texturised"];
|
|
1455
1599
|
var BRUSH_FLIPS = ["none", "x", "y", "xy"];
|
|
1600
|
+
var GHOST_TRIGGERS2 = ["immediate", "agent"];
|
|
1456
1601
|
|
|
1457
1602
|
// src/brushes/schema.ts
|
|
1458
1603
|
var MAX_BRUSH_PALETTE = 16;
|
|
@@ -1645,9 +1790,28 @@ var LIGHT = object("Light the brush gives off with its colour (glow and spill).
|
|
|
1645
1790
|
}),
|
|
1646
1791
|
glow: bool("Suggest a glow layer effect for the layer (glow stays a layer effect).")
|
|
1647
1792
|
});
|
|
1793
|
+
var FLUORESCENCE = object("UV-fluorescent ink: glows in its colour under the virtual UV blacklight (set_uv_light); invisible in normal light when invisible is true.", {
|
|
1794
|
+
amount: unit3("Fluorescence intensity under UV blacklight, 0 (none) .. 1."),
|
|
1795
|
+
invisible: bool("Invisible ink in normal light: lays no daylight paint and glows only under UV (default false).")
|
|
1796
|
+
});
|
|
1797
|
+
var THERMOCHROMIC = object("Thermochromic ink: shifts smoothly toward hotColor where the canvas heat field warms it past threshold.", {
|
|
1798
|
+
hotColor: { type: "string", pattern: "^#[0-9a-fA-F]{6}$", description: "Colour (#rrggbb) the ink turns when heated." },
|
|
1799
|
+
threshold: num(0.05, 0.95, "Temperature 0.05..0.95 at which the colour shift is halfway (default 0.5).")
|
|
1800
|
+
});
|
|
1801
|
+
var IRIDESCENCE = object("Structural colour: a thin dielectric film over the paint whose interference hue shifts with the light and view tilt. Missing fields take their defaults.", {
|
|
1802
|
+
thickness: num(100, 1e3, "Film thickness in nanometres, 100..1000 (default 400)."),
|
|
1803
|
+
pressureToThickness: unit3("How much pen pressure shifts the film thickness per dab, 0..1 (default 0: uniform thickness)."),
|
|
1804
|
+
ior: num(1.2, 2, "Refractive index of the film, 1.2..2.0 (default 1.5; water/soap ~1.33, pearl ~1.55, beetle lacquer ~1.65).")
|
|
1805
|
+
});
|
|
1648
1806
|
var GHOST = object('Ghost paint: wet paint that flows and mixes visibly while wet, then fades away without leaving its colour, keeping only the smear it made in the paint it touched (needs behaviour.surface "wet").', {
|
|
1649
1807
|
linger: num(0.5, 60, "Seconds of simulated time before the ghost paint has fully faded (default 4)."),
|
|
1650
|
-
smear: unit3("How much the ghost paint drags and lifts the paint it passes through: 0 none .. 1 full (default 0.3).")
|
|
1808
|
+
smear: unit3("How much the ghost paint drags and lifts the paint it passes through: 0 none .. 1 full (default 0.3)."),
|
|
1809
|
+
trigger: oneOf2(GHOST_TRIGGERS2, "When the fade starts: immediate (default) counts down as soon as the paint is laid; agent holds the fade until read_mutation claims the stroke.")
|
|
1810
|
+
});
|
|
1811
|
+
var MUTATION = object("Mutation paint: you paint something and pause; once an agent claims it with read_mutation, your paint sinks away and the agent paints its own answer (any wet or dry paint brush that lays its own colour without reading paint beneath).", {
|
|
1812
|
+
settle: num(0.5, 10, "Seconds after the last stroke before it counts as finished and raises a mutation request (default 1.5)."),
|
|
1813
|
+
patience: num(5, 300, "Seconds to wait for an agent before giving up and keeping the paint as ordinary paint (default 45)."),
|
|
1814
|
+
fade: num(0.5, 10, "Seconds the paint takes to sink away once claimed by read_mutation (default 2).")
|
|
1651
1815
|
});
|
|
1652
1816
|
var MOTION = object("Animation after painting.", {
|
|
1653
1817
|
live: bool("Effect paint keeps playing on stroke layers (default false)."),
|
|
@@ -1712,7 +1876,11 @@ var BRUSH_JSON_SCHEMA = object(
|
|
|
1712
1876
|
dynamics: DYNAMICS,
|
|
1713
1877
|
behaviour: BEHAVIOUR,
|
|
1714
1878
|
light: LIGHT,
|
|
1879
|
+
fluorescence: FLUORESCENCE,
|
|
1880
|
+
thermochromic: THERMOCHROMIC,
|
|
1881
|
+
iridescence: IRIDESCENCE,
|
|
1715
1882
|
ghost: GHOST,
|
|
1883
|
+
mutation: MUTATION,
|
|
1716
1884
|
motion: MOTION,
|
|
1717
1885
|
colour: COLOUR
|
|
1718
1886
|
},
|
|
@@ -1725,7 +1893,11 @@ var BRUSH_FACET_SCHEMAS = {
|
|
|
1725
1893
|
dynamics: DYNAMICS,
|
|
1726
1894
|
behaviour: BEHAVIOUR,
|
|
1727
1895
|
light: LIGHT,
|
|
1896
|
+
fluorescence: FLUORESCENCE,
|
|
1897
|
+
thermochromic: THERMOCHROMIC,
|
|
1898
|
+
iridescence: IRIDESCENCE,
|
|
1728
1899
|
ghost: GHOST,
|
|
1900
|
+
mutation: MUTATION,
|
|
1729
1901
|
motion: MOTION,
|
|
1730
1902
|
colour: COLOUR,
|
|
1731
1903
|
paint: PAINT
|
|
@@ -1968,11 +2140,13 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
1968
2140
|
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).`);
|
|
1969
2141
|
if (kind === "tool" && !tool) throw new Error(`Brush "${raw.id}" is a tool and needs tool: one of ${BRUSH_TOOLS.join(", ")}.`);
|
|
1970
2142
|
const { colour: rawColour, marks: rawMarks, kind: _k, id: _i, name: _n, tool: _t, ...rest } = raw;
|
|
2143
|
+
if (typeof rest.fluorescence === "number" && Number.isFinite(rest.fluorescence)) rest.fluorescence = { amount: rest.fluorescence };
|
|
1971
2144
|
const out = readObject(rest, BRUSH_JSON_SCHEMA, "brush", problems);
|
|
2145
|
+
if (_optionalChain([out, 'access', _29 => _29.thermochromic, 'optionalAccess', _30 => _30.hotColor])) out.thermochromic.hotColor = out.thermochromic.hotColor.toLowerCase();
|
|
1972
2146
|
const brush = { id: raw.id, name, ...out, kind, ...tool ? { tool } : {}, tip: _nullishCoalesce(out.tip, () => ( { shape: "round" })) };
|
|
1973
2147
|
const ordered = () => {
|
|
1974
|
-
const { id, name: n, description, tags, kind: k, medium, variety, tool: t, tip, marks, dynamics, behaviour, light, ghost, motion, colour } = brush;
|
|
1975
|
-
const all = { id, name: n, description, tags, kind: k, medium, variety, tool: t, tip, marks, dynamics, behaviour, light, ghost, motion, colour };
|
|
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 };
|
|
1976
2150
|
return Object.fromEntries(Object.entries(all).filter(([, v]) => v !== void 0));
|
|
1977
2151
|
};
|
|
1978
2152
|
if (!out.tip) problems.push("tip is missing; a round tip is used.");
|
|
@@ -2020,16 +2194,24 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2020
2194
|
drop("marks", "tools lay no marks");
|
|
2021
2195
|
delete brush.marks;
|
|
2022
2196
|
}
|
|
2197
|
+
if (brush.iridescence) {
|
|
2198
|
+
drop("iridescence", "tools lay no film");
|
|
2199
|
+
delete brush.iridescence;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
if (brush.iridescence && _optionalChain([brush, 'access', _31 => _31.marks, 'optionalAccess', _32 => _32.kind]) === "objects") {
|
|
2203
|
+
drop("iridescence", "object marks are stickers, not film");
|
|
2204
|
+
delete brush.iridescence;
|
|
2023
2205
|
}
|
|
2024
|
-
if (_optionalChain([brush, 'access',
|
|
2025
|
-
const surface = _optionalChain([brush, 'access',
|
|
2026
|
-
const why = brush.tip.shape === "bristle" ? "bristle tips lay their own marks" : surface === "wet" || surface === "thick" ? `${surface} paint is not sprayed` : _optionalChain([brush, 'access',
|
|
2206
|
+
if (_optionalChain([brush, 'access', _33 => _33.marks, 'optionalAccess', _34 => _34.kind]) === "scatter") {
|
|
2207
|
+
const surface = _optionalChain([brush, 'access', _35 => _35.behaviour, 'optionalAccess', _36 => _36.surface]);
|
|
2208
|
+
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;
|
|
2027
2209
|
if (why) {
|
|
2028
2210
|
drop("marks", why);
|
|
2029
2211
|
delete brush.marks;
|
|
2030
2212
|
}
|
|
2031
2213
|
}
|
|
2032
|
-
if (_optionalChain([brush, 'access',
|
|
2214
|
+
if (_optionalChain([brush, 'access', _40 => _40.marks, 'optionalAccess', _41 => _41.kind]) === "objects" && brush.tip.shape !== "stamp") {
|
|
2033
2215
|
problems.push("marks objects lay stamps: tip.shape set to stamp.");
|
|
2034
2216
|
brush.tip.shape = "stamp";
|
|
2035
2217
|
}
|
|
@@ -2043,7 +2225,7 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2043
2225
|
}
|
|
2044
2226
|
if (brush.tip.stamps) {
|
|
2045
2227
|
let stamps = brush.tip.stamps.map((s) => _chunkRDFHWAHLcjs.normalizeStickerRef.call(void 0, s));
|
|
2046
|
-
if (_optionalChain([brush, 'access',
|
|
2228
|
+
if (_optionalChain([brush, 'access', _42 => _42.marks, 'optionalAccess', _43 => _43.kind]) === "objects" && stamps.some(isBrushSourceRef)) {
|
|
2047
2229
|
problems.push("tip.stamps: image sources (source:\u2026) dropped: objects marks lay stickers; use continuous, scatter or particles marks for an image tip.");
|
|
2048
2230
|
stamps = stamps.filter((s) => !isBrushSourceRef(s));
|
|
2049
2231
|
}
|
|
@@ -2054,8 +2236,8 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2054
2236
|
}
|
|
2055
2237
|
}
|
|
2056
2238
|
if (brush.tip.grain) {
|
|
2057
|
-
const surface = _optionalChain([brush, 'access',
|
|
2058
|
-
const marks = _optionalChain([brush, 'access',
|
|
2239
|
+
const surface = _optionalChain([brush, 'access', _44 => _44.behaviour, 'optionalAccess', _45 => _45.surface]);
|
|
2240
|
+
const marks = _optionalChain([brush, 'access', _46 => _46.marks, 'optionalAccess', _47 => _47.kind]);
|
|
2059
2241
|
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;
|
|
2060
2242
|
if (why) {
|
|
2061
2243
|
drop("tip.grain", why);
|
|
@@ -2092,13 +2274,13 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2092
2274
|
if (b.exchange && kind === "tool" && !wetTool) {
|
|
2093
2275
|
drop("behaviour.exchange", `the ${brush.tool} does not pick paint up`);
|
|
2094
2276
|
delete b.exchange;
|
|
2095
|
-
} else if (b.exchange && (_optionalChain([brush, 'access',
|
|
2277
|
+
} 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")) {
|
|
2096
2278
|
drop("behaviour.exchange", "particle, object and bristle marks do not pick paint up");
|
|
2097
2279
|
delete b.exchange;
|
|
2098
2280
|
}
|
|
2099
2281
|
}
|
|
2100
2282
|
if (brush.ghost) {
|
|
2101
|
-
if (kind === "tool" || _optionalChain([brush, 'access',
|
|
2283
|
+
if (kind === "tool" || _optionalChain([brush, 'access', _52 => _52.behaviour, 'optionalAccess', _53 => _53.surface]) !== "wet") {
|
|
2102
2284
|
drop("ghost", "it needs a mark brush with behaviour.surface wet");
|
|
2103
2285
|
delete brush.ghost;
|
|
2104
2286
|
} else {
|
|
@@ -2106,7 +2288,7 @@ function normalizeBrushSpec(raw, problems = []) {
|
|
|
2106
2288
|
drop("light", "ghost paint uses the wet light channel as its tracer");
|
|
2107
2289
|
delete brush.light;
|
|
2108
2290
|
}
|
|
2109
|
-
if (_optionalChain([brush, 'access',
|
|
2291
|
+
if (_optionalChain([brush, 'access', _54 => _54.behaviour, 'optionalAccess', _55 => _55.exchange])) {
|
|
2110
2292
|
drop("behaviour.exchange", "ghost paint does not pick paint up");
|
|
2111
2293
|
delete brush.behaviour.exchange;
|
|
2112
2294
|
}
|
|
@@ -2135,7 +2317,8 @@ var PAINT_PROFILE = {
|
|
|
2135
2317
|
velocity: "full",
|
|
2136
2318
|
tilt: "full",
|
|
2137
2319
|
tipGrain: "full",
|
|
2138
|
-
dabCount: "full"
|
|
2320
|
+
dabCount: "full",
|
|
2321
|
+
iridescence: "full"
|
|
2139
2322
|
};
|
|
2140
2323
|
var IMAGE_PROFILE = {
|
|
2141
2324
|
id: "image",
|
|
@@ -2154,7 +2337,8 @@ var IMAGE_PROFILE = {
|
|
|
2154
2337
|
velocity: "ignored",
|
|
2155
2338
|
tilt: "ignored",
|
|
2156
2339
|
tipGrain: "ignored",
|
|
2157
|
-
dabCount: "ignored"
|
|
2340
|
+
dabCount: "ignored",
|
|
2341
|
+
iridescence: "none"
|
|
2158
2342
|
};
|
|
2159
2343
|
var CANVAS_PROFILE = {
|
|
2160
2344
|
id: "canvas",
|
|
@@ -2173,7 +2357,8 @@ var CANVAS_PROFILE = {
|
|
|
2173
2357
|
velocity: "ignored",
|
|
2174
2358
|
tilt: "full",
|
|
2175
2359
|
tipGrain: "ignored",
|
|
2176
|
-
dabCount: "ignored"
|
|
2360
|
+
dabCount: "ignored",
|
|
2361
|
+
iridescence: "none"
|
|
2177
2362
|
};
|
|
2178
2363
|
var VECTOR_PROFILE = { ...CANVAS_PROFILE, id: "vector", name: "Vector editor", kinds: ["mark", "tool"], tools: ["blender", "smudge"], surfaces: ["dry", "wet"], reservoir: "full", light: "ignored" };
|
|
2179
2364
|
var BRUSH_HOST_PROFILES = { paint: PAINT_PROFILE, image: IMAGE_PROFILE, canvas: CANVAS_PROFILE, vector: VECTOR_PROFILE };
|
|
@@ -2201,16 +2386,28 @@ function checkBrush(brush, profile) {
|
|
|
2201
2386
|
const image = brush.tip.shape === "stamp" && (_nullishCoalesce(brush.tip.stamps, () => ( []))).some(isBrushSourceRef);
|
|
2202
2387
|
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)}.`);
|
|
2203
2388
|
}
|
|
2204
|
-
const marks = _nullishCoalesce(_optionalChain([brush, 'access',
|
|
2389
|
+
const marks = _nullishCoalesce(_optionalChain([brush, 'access', _56 => _56.marks, 'optionalAccess', _57 => _57.kind]), () => ( "continuous"));
|
|
2205
2390
|
if (!profile.marks.includes(marks)) add("marks", "error", `${host} can't lay ${marks} marks.`, `Use marks ${list(profile.marks)} (remove marks for continuous dabs).`);
|
|
2206
|
-
const surface = _nullishCoalesce(_optionalChain([brush, 'access',
|
|
2391
|
+
const surface = _nullishCoalesce(_optionalChain([brush, 'access', _58 => _58.behaviour, 'optionalAccess', _59 => _59.surface]), () => ( "dry"));
|
|
2207
2392
|
if (brush.kind === "mark" && !profile.surfaces.includes(surface)) add("behaviour.surface", "error", `${host} has no ${surface} surface.`, `Set behaviour.surface to ${list(profile.surfaces)}.`);
|
|
2208
|
-
const ex = _optionalChain([brush, 'access',
|
|
2209
|
-
feature(profile.reservoir, !!_optionalChain([ex, 'optionalAccess',
|
|
2210
|
-
feature(profile.exchange, !_optionalChain([ex, 'optionalAccess',
|
|
2211
|
-
feature(profile.grain, nonZero(_optionalChain([brush, 'access',
|
|
2212
|
-
feature(profile.light, nonZero(_optionalChain([brush, 'access',
|
|
2393
|
+
const ex = _optionalChain([brush, 'access', _60 => _60.behaviour, 'optionalAccess', _61 => _61.exchange]);
|
|
2394
|
+
feature(profile.reservoir, !!_optionalChain([ex, 'optionalAccess', _62 => _62.reservoir]), "behaviour.exchange.reservoir", "a Mixer Brush reservoir", "Remove behaviour.exchange.reservoir.");
|
|
2395
|
+
feature(profile.exchange, !_optionalChain([ex, 'optionalAccess', _63 => _63.reservoir]) && (nonZero(_optionalChain([ex, 'optionalAccess', _64 => _64.pickup])) || !!_optionalChain([ex, 'optionalAccess', _65 => _65.dirty])), "behaviour.exchange", "picking paint up", "Remove behaviour.exchange.");
|
|
2396
|
+
feature(profile.grain, nonZero(_optionalChain([brush, 'access', _66 => _66.behaviour, 'optionalAccess', _67 => _67.grain])), "behaviour.grain", "paper grain", "Set behaviour.grain to 0.");
|
|
2397
|
+
feature(profile.light, nonZero(_optionalChain([brush, 'access', _68 => _68.light, 'optionalAccess', _69 => _69.amount])), "light", "light paint (glow and spill)", "Remove light (or set light.amount to 0).");
|
|
2398
|
+
const smartInks = profile.id === "paint" ? "full" : "none";
|
|
2399
|
+
feature(smartInks, nonZero(_optionalChain([brush, 'access', _70 => _70.fluorescence, 'optionalAccess', _71 => _71.amount])), "fluorescence", "UV-fluorescent ink", "Remove fluorescence (or set fluorescence.amount to 0).");
|
|
2400
|
+
feature(smartInks, Boolean(_optionalChain([brush, 'access', _72 => _72.thermochromic, 'optionalAccess', _73 => _73.hotColor])), "thermochromic", "thermochromic ink", "Remove thermochromic.");
|
|
2401
|
+
feature(_nullishCoalesce(profile.iridescence, () => ( "none")), !!brush.iridescence, "iridescence", "thin-film iridescence", "Remove iridescence.");
|
|
2213
2402
|
feature(profile.surfaces.includes("wet") && profile.light === "full" ? "full" : "none", !!brush.ghost, "ghost", "ghost paint", "Remove ghost.");
|
|
2403
|
+
if (brush.mutation) {
|
|
2404
|
+
if (profile.id !== "paint") feature("none", true, "mutation", "mutation paint", "Remove mutation.");
|
|
2405
|
+
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', _74 => _74.marks, 'optionalAccess', _75 => _75.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.");
|
|
2407
|
+
else if (_optionalChain([ex, 'optionalAccess', _76 => _76.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.");
|
|
2408
|
+
else if (nonZero(_optionalChain([ex, 'optionalAccess', _77 => _77.pickup])) || !!_optionalChain([ex, 'optionalAccess', _78 => _78.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.");
|
|
2409
|
+
else if (surface === "wet" && nonZero(_optionalChain([brush, 'access', _79 => _79.light, 'optionalAccess', _80 => _80.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
|
+
}
|
|
2214
2411
|
const m = brush.motion;
|
|
2215
2412
|
feature(profile.motion, !!m && (!!m.live || nonZero(m.waveSpeed) || m.fps !== void 0), "motion", "animation", "Remove motion.");
|
|
2216
2413
|
const d = _nullishCoalesce(brush.dynamics, () => ( {}));
|
|
@@ -2219,16 +2416,16 @@ function checkBrush(brush, profile) {
|
|
|
2219
2416
|
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.");
|
|
2220
2417
|
feature(profile.tilt, nonZero(d.tiltSize) || nonZero(d.tiltRoundness) || nonZero(d.tiltOpacity), "dynamics.tilt", "tilt dynamics", "Set dynamics.tiltSize, tiltRoundness and tiltOpacity to 0.");
|
|
2221
2418
|
const colour = brush.colour;
|
|
2222
|
-
if (_optionalChain([colour, 'optionalAccess',
|
|
2419
|
+
if (_optionalChain([colour, 'optionalAccess', _81 => _81.default]) !== void 0) {
|
|
2223
2420
|
const kind = brushColourKind(colour.default);
|
|
2224
2421
|
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.`);
|
|
2225
2422
|
}
|
|
2226
|
-
const foreign = [...new Set((_nullishCoalesce(_optionalChain([colour, 'optionalAccess',
|
|
2423
|
+
const foreign = [...new Set((_nullishCoalesce(_optionalChain([colour, 'optionalAccess', _82 => _82.palette]), () => ( []))).map(brushColourKind).filter((k) => !profile.colours.includes(k)))];
|
|
2227
2424
|
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.`);
|
|
2228
2425
|
return { ok: !issues.some((i) => i.severity === "error"), issues };
|
|
2229
2426
|
}
|
|
2230
2427
|
function withKeptIssues(check, before) {
|
|
2231
|
-
const had = new Set((_nullishCoalesce(_optionalChain([before, 'optionalAccess',
|
|
2428
|
+
const had = new Set((_nullishCoalesce(_optionalChain([before, 'optionalAccess', _83 => _83.issues]), () => ( []))).filter((i) => i.severity === "error").map((i) => i.facet));
|
|
2232
2429
|
const kept = check.issues.filter((i) => i.severity === "error" && had.has(i.facet));
|
|
2233
2430
|
const issues = check.issues.filter((i) => !kept.includes(i));
|
|
2234
2431
|
return { ok: !issues.some((i) => i.severity === "error"), issues, kept };
|
|
@@ -2266,6 +2463,9 @@ function degrade(brush, profile, options = {}) {
|
|
|
2266
2463
|
case "ghost":
|
|
2267
2464
|
delete b.ghost;
|
|
2268
2465
|
break;
|
|
2466
|
+
case "mutation":
|
|
2467
|
+
delete b.mutation;
|
|
2468
|
+
break;
|
|
2269
2469
|
case "behaviour.exchange.reservoir":
|
|
2270
2470
|
delete b.behaviour.exchange.reservoir;
|
|
2271
2471
|
break;
|
|
@@ -2284,6 +2484,15 @@ function degrade(brush, profile, options = {}) {
|
|
|
2284
2484
|
case "light":
|
|
2285
2485
|
delete b.light;
|
|
2286
2486
|
break;
|
|
2487
|
+
case "fluorescence":
|
|
2488
|
+
delete b.fluorescence;
|
|
2489
|
+
break;
|
|
2490
|
+
case "thermochromic":
|
|
2491
|
+
delete b.thermochromic;
|
|
2492
|
+
break;
|
|
2493
|
+
case "iridescence":
|
|
2494
|
+
delete b.iridescence;
|
|
2495
|
+
break;
|
|
2287
2496
|
case "motion":
|
|
2288
2497
|
delete b.motion;
|
|
2289
2498
|
break;
|
|
@@ -2308,7 +2517,7 @@ function degrade(brush, profile, options = {}) {
|
|
|
2308
2517
|
}
|
|
2309
2518
|
dropped.push(issue);
|
|
2310
2519
|
}
|
|
2311
|
-
if (_optionalChain([b, 'access',
|
|
2520
|
+
if (_optionalChain([b, 'access', _84 => _84.behaviour, 'optionalAccess', _85 => _85.exchange]) && !Object.keys(b.behaviour.exchange).length) delete b.behaviour.exchange;
|
|
2312
2521
|
if (b.colour && !Object.keys(b.colour).length) delete b.colour;
|
|
2313
2522
|
const after = checkBrush(b, profile).issues.filter((i) => i.severity === "error");
|
|
2314
2523
|
return { brush: b, dropped, issues: after };
|
|
@@ -2352,7 +2561,7 @@ function parseBrushLibrary2(raw, problems = []) {
|
|
|
2352
2561
|
if (file && file.version !== void 0 && file.version !== 1 && file.version !== 2) {
|
|
2353
2562
|
throw new BrushLibraryError("invalid", `Brush library version ${JSON.stringify(file.version)} is not supported; this library reads versions 1 and 2.`);
|
|
2354
2563
|
}
|
|
2355
|
-
const v1File = _optionalChain([file, 'optionalAccess',
|
|
2564
|
+
const v1File = _optionalChain([file, 'optionalAccess', _86 => _86.version]) === 1;
|
|
2356
2565
|
const out = [];
|
|
2357
2566
|
items.forEach((item, i) => {
|
|
2358
2567
|
const at = `brushes[${i}]`;
|
|
@@ -2613,7 +2822,7 @@ function snapshotBrushLibrary(library, onChange) {
|
|
|
2613
2822
|
(list2) => {
|
|
2614
2823
|
entries = list2;
|
|
2615
2824
|
user = list2.filter((e) => !e.builtin).map((e) => e.brush);
|
|
2616
|
-
_optionalChain([onChange, 'optionalCall',
|
|
2825
|
+
_optionalChain([onChange, 'optionalCall', _87 => _87()]);
|
|
2617
2826
|
},
|
|
2618
2827
|
() => void 0
|
|
2619
2828
|
);
|
|
@@ -2746,4 +2955,13 @@ function snapshotBrushLibrary(library, onChange) {
|
|
|
2746
2955
|
|
|
2747
2956
|
|
|
2748
2957
|
|
|
2749
|
-
|
|
2958
|
+
|
|
2959
|
+
|
|
2960
|
+
|
|
2961
|
+
|
|
2962
|
+
|
|
2963
|
+
|
|
2964
|
+
|
|
2965
|
+
|
|
2966
|
+
|
|
2967
|
+
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.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;
|
|
@@ -74,7 +74,7 @@ function profileAlongPolyline(points, values, count = 32) {
|
|
|
74
74
|
var round3 = (v) => Math.round(v * 1e3) / 1e3;
|
|
75
75
|
var loading = null;
|
|
76
76
|
function loadCanvasBrushstrokeRenderer() {
|
|
77
|
-
loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-
|
|
77
|
+
loading ??= Promise.resolve().then(() => _interopRequireWildcard(require("./brushstrokeRender-2RQL46WB.cjs"))).catch((error) => {
|
|
78
78
|
loading = null;
|
|
79
79
|
throw error;
|
|
80
80
|
});
|