@pixerate/editor 0.245.0 → 0.248.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-editor/index.cjs +59 -59
- package/dist/animation-editor/index.js +3 -3
- package/dist/{brushstrokeRender-QA5WVIDT.cjs → brushstrokeRender-DTDPFBLC.cjs} +5 -5
- package/dist/{brushstrokeRender-4542T5HP.js → brushstrokeRender-QLCXROJK.js} +2 -2
- package/dist/canvas/index.cjs +8 -8
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-OSTKS3YN.js → chunk-5GLYUSNT.js} +1 -1
- package/dist/{chunk-4T47Z4J3.cjs → chunk-6G6ZDDDB.cjs} +400 -27
- package/dist/chunk-7T7JWTVE.cjs +192 -0
- package/dist/{chunk-SD23XMVJ.cjs → chunk-IBDU6EGZ.cjs} +35 -35
- package/dist/{chunk-IZL5V5RK.cjs → chunk-L6REDLNB.cjs} +2 -2
- package/dist/{chunk-L5PB7EG3.js → chunk-NZ3O3A5C.js} +1 -1
- package/dist/{chunk-4EQY4TZJ.js → chunk-Q2HA52V4.js} +1 -1
- package/dist/chunk-SC2RZ5EF.js +192 -0
- package/dist/{chunk-HZ547SGQ.cjs → chunk-TPWB4Y6L.cjs} +1 -1
- package/dist/{chunk-TBJEMELS.js → chunk-VNBUDVD4.js} +398 -25
- package/dist/{chunk-M6JORA5Z.cjs → chunk-WVS5KQDH.cjs} +442 -87
- package/dist/{chunk-5RFENYO5.js → chunk-XNRBMYNB.js} +1 -1
- package/dist/{chunk-BDIH4DYR.cjs → chunk-XWTRATIM.cjs} +23 -23
- package/dist/{chunk-7YQNKSQ7.js → chunk-ZFSLEOSQ.js} +357 -2
- package/dist/image-editor/effect-tools.d.cts +37 -4
- package/dist/image-editor/effect-tools.d.ts +37 -4
- package/dist/image-editor/index.cjs +1059 -1552
- package/dist/image-editor/index.d.cts +1 -1
- package/dist/image-editor/index.d.ts +1 -1
- package/dist/image-editor/index.js +275 -768
- package/dist/paint-editor/controller.d.cts +34 -1
- package/dist/paint-editor/controller.d.ts +34 -1
- package/dist/paint-editor/index.cjs +426 -257
- package/dist/paint-editor/index.js +184 -15
- package/dist/paint-editor/procedural.d.cts +82 -5
- package/dist/paint-editor/procedural.d.ts +82 -5
- package/dist/paint-editor/tools.d.cts +1 -1
- package/dist/paint-editor/tools.d.ts +1 -1
- package/dist/paint-editor/ui.d.cts +15 -2
- package/dist/paint-editor/ui.d.ts +15 -2
- package/dist/{image-editor/layers/effects-ui.d.cts → raster/effects/ui.d.cts} +32 -5
- package/dist/{image-editor/layers/effects-ui.d.ts → raster/effects/ui.d.ts} +32 -5
- package/dist/raster/index.cjs +13 -1
- package/dist/raster/index.d.cts +1 -0
- package/dist/raster/index.d.ts +1 -0
- package/dist/raster/index.js +12 -0
- package/dist/vector/index.cjs +962 -962
- package/dist/vector/index.js +4 -4
- package/package.json +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +18 -27
- package/skills/pixerate-paint-editor/references/gradient-paint.md +23 -0
- package/skills/pixerate-paint-editor/references/layer-effects.md +28 -0
- package/skills/pixerate-paint-editor/references/light-paint.md +10 -2
- package/skills/pixerate-paint-editor/references/particle-brushes.md +66 -0
|
@@ -964,9 +964,56 @@ var MarkStroke = (_class5 = class {
|
|
|
964
964
|
const rect = _chunkPQ3H2AANcjs.dabRect.call(void 0, dab, hardness);
|
|
965
965
|
const n = rect.width * rect.height;
|
|
966
966
|
if (n <= 0) return;
|
|
967
|
+
const vals = this.scratch(n);
|
|
968
|
+
_chunkPQ3H2AANcjs.rasterizeDab.call(void 0, dab, hardness, rect, vals);
|
|
969
|
+
this.lay(rect, vals, dab.opacity, pressure, x, y, rects);
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Stamps a textured mark: `kind` from the sticker stamps over a square of half-size `r`, turned
|
|
973
|
+
* `rotation` radians clockwise, with flow `alpha` towards `opacity`. Small marks are supersampled.
|
|
974
|
+
*/
|
|
975
|
+
stamp(x, y, r, kind, rotation, seed, alpha, opacity, pressure, rects) {
|
|
976
|
+
if (!(r > 0.05) || !(alpha > 0)) return;
|
|
977
|
+
const reach = Math.max(0.5, r) * Math.SQRT2 + 1;
|
|
978
|
+
const rect = { x: Math.floor(x - reach), y: Math.floor(y - reach), width: 0, height: 0 };
|
|
979
|
+
rect.width = Math.ceil(x + reach) - rect.x;
|
|
980
|
+
rect.height = Math.ceil(y + reach) - rect.y;
|
|
981
|
+
const n = rect.width * rect.height;
|
|
982
|
+
const vals = this.scratch(n);
|
|
983
|
+
const ss = r < 10 ? 3 : 1;
|
|
984
|
+
const c = Math.cos(rotation);
|
|
985
|
+
const s = Math.sin(rotation);
|
|
986
|
+
const inv = 1 / Math.max(0.5, r);
|
|
987
|
+
const a = Math.min(1, alpha);
|
|
988
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
989
|
+
for (let xx = 0; xx < rect.width; xx++, k++) {
|
|
990
|
+
let sum = 0;
|
|
991
|
+
for (let sy = 0; sy < ss; sy++) {
|
|
992
|
+
for (let sx = 0; sx < ss; sx++) {
|
|
993
|
+
const dx = rect.x + xx + (sx + 0.5) / ss - x;
|
|
994
|
+
const dy = rect.y + yy + (sy + 0.5) / ss - y;
|
|
995
|
+
sum += stampCoverage(kind, (dx * c + dy * s) * inv, (dy * c - dx * s) * inv, seed);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
vals[k] = sum / (ss * ss) * a;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
this.lay(rect, vals, Math.min(1, opacity), pressure, x, y, rects);
|
|
1002
|
+
}
|
|
1003
|
+
/** A scratch buffer of `n` values (reused; valid until the next mark). */
|
|
1004
|
+
scratch(n) {
|
|
967
1005
|
if (this.buf.length < n) this.buf = new Float32Array(n);
|
|
968
1006
|
const vals = this.buf.subarray(0, n);
|
|
969
|
-
|
|
1007
|
+
vals.fill(0);
|
|
1008
|
+
return vals;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Lays coverage `vals` over `rect` towards `opacity`: paper grain, then effect paint or the
|
|
1012
|
+
* colour of a mark at (x, y) (gradient paint), into the stroke's coverage.
|
|
1013
|
+
*/
|
|
1014
|
+
lay(rect, vals, opacity, pressure, x, y, rects) {
|
|
1015
|
+
const n = rect.width * rect.height;
|
|
1016
|
+
if (n <= 0) return;
|
|
970
1017
|
const grain = this.options.grain;
|
|
971
1018
|
if (this.depth > 0 && grain) {
|
|
972
1019
|
for (let yy = 0, k = 0; yy < rect.height; yy++)
|
|
@@ -976,11 +1023,11 @@ var MarkStroke = (_class5 = class {
|
|
|
976
1023
|
if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
|
|
977
1024
|
const field = this.field.subarray(0, n * 3);
|
|
978
1025
|
sampleEffectField(this.options.effect, rect, vals, field, this.pen);
|
|
979
|
-
this.coverage.accumulate(rect, vals,
|
|
1026
|
+
this.coverage.accumulate(rect, vals, opacity, null, field);
|
|
980
1027
|
} else {
|
|
981
1028
|
const color = this.markColor(x, y);
|
|
982
1029
|
if (color && color.alpha < 1) for (let k = 0; k < n; k++) vals[k] *= color.alpha;
|
|
983
|
-
this.coverage.accumulate(rect, vals,
|
|
1030
|
+
this.coverage.accumulate(rect, vals, opacity, color ? color.rgb : null);
|
|
984
1031
|
}
|
|
985
1032
|
rects.push(rect);
|
|
986
1033
|
this.dabCount++;
|
|
@@ -993,14 +1040,41 @@ var MarkStroke = (_class5 = class {
|
|
|
993
1040
|
for (let i = 1; i <= n; i++) this.mark(x0 + (x1 - x0) * i / n, y0 + (y1 - y0) * i / n, r, alpha, opacity, pressure, rects);
|
|
994
1041
|
}
|
|
995
1042
|
}, _class5);
|
|
996
|
-
var PARTICLE_KINDS = ["string", "spring", "orbit", "flow"];
|
|
997
|
-
var PARTICLE_RENDERS = ["dot", "line", "sequence"];
|
|
998
|
-
var
|
|
1043
|
+
var PARTICLE_KINDS = ["string", "spring", "orbit", "flow", "elastic", "fuzzy"];
|
|
1044
|
+
var PARTICLE_RENDERS = ["dot", "line", "sequence", "ribbon"];
|
|
1045
|
+
var RIBBON_PAIRS = ["adjacent", "first-last"];
|
|
1046
|
+
var DOT_TEXTURE_ORDERS = ["sequence", "random"];
|
|
1047
|
+
var DOT_ROTATIONS = ["follow", "random", "fixed"];
|
|
1048
|
+
var MAX_DOT_TEXTURES = 4;
|
|
1049
|
+
var DEFAULT_PARTICLE = {
|
|
1050
|
+
kind: "spring",
|
|
1051
|
+
render: "line",
|
|
1052
|
+
count: 12,
|
|
1053
|
+
size: 0.08,
|
|
1054
|
+
spread: 0.5,
|
|
1055
|
+
stiffness: 0.4,
|
|
1056
|
+
damping: 0.25,
|
|
1057
|
+
speed: 0.5,
|
|
1058
|
+
gravity: 0,
|
|
1059
|
+
life: 0.5,
|
|
1060
|
+
interval: 0.3,
|
|
1061
|
+
ribbonFill: 0.5,
|
|
1062
|
+
ribbonPairs: "adjacent",
|
|
1063
|
+
waveAmplitude: 0,
|
|
1064
|
+
waveLength: 60,
|
|
1065
|
+
waveSpeed: 0,
|
|
1066
|
+
wavePhaseJitter: 0,
|
|
1067
|
+
dotTextures: [],
|
|
1068
|
+
dotTextureOrder: "sequence",
|
|
1069
|
+
dotRotation: "random"
|
|
1070
|
+
};
|
|
999
1071
|
var unit3 = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
|
|
1072
|
+
var oneOf = (v, all, d) => all.includes(v) ? v : d;
|
|
1000
1073
|
function normalizeParticle(raw = {}, base = DEFAULT_PARTICLE) {
|
|
1074
|
+
const textures = Array.isArray(raw.dotTextures) ? raw.dotTextures.filter((t) => STAMP_KINDS.includes(t)).slice(0, MAX_DOT_TEXTURES) : [..._nullishCoalesce(base.dotTextures, () => ( []))];
|
|
1001
1075
|
return {
|
|
1002
|
-
kind:
|
|
1003
|
-
render:
|
|
1076
|
+
kind: oneOf(raw.kind, PARTICLE_KINDS, base.kind),
|
|
1077
|
+
render: oneOf(raw.render, PARTICLE_RENDERS, base.render),
|
|
1004
1078
|
count: typeof raw.count === "number" && Number.isFinite(raw.count) ? Math.min(64, Math.max(1, Math.round(raw.count))) : base.count,
|
|
1005
1079
|
size: unit3(raw.size, base.size),
|
|
1006
1080
|
spread: unit3(raw.spread, base.spread),
|
|
@@ -1009,9 +1083,61 @@ function normalizeParticle(raw = {}, base = DEFAULT_PARTICLE) {
|
|
|
1009
1083
|
speed: unit3(raw.speed, base.speed),
|
|
1010
1084
|
gravity: typeof raw.gravity === "number" && Number.isFinite(raw.gravity) ? Math.min(1, Math.max(-1, raw.gravity)) : base.gravity,
|
|
1011
1085
|
life: unit3(raw.life, base.life),
|
|
1012
|
-
interval: unit3(raw.interval, base.interval)
|
|
1086
|
+
interval: unit3(raw.interval, base.interval),
|
|
1087
|
+
ribbonFill: unit3(raw.ribbonFill, _nullishCoalesce(base.ribbonFill, () => ( DEFAULT_PARTICLE.ribbonFill))),
|
|
1088
|
+
ribbonPairs: oneOf(raw.ribbonPairs, RIBBON_PAIRS, _nullishCoalesce(base.ribbonPairs, () => ( DEFAULT_PARTICLE.ribbonPairs))),
|
|
1089
|
+
waveAmplitude: unit3(raw.waveAmplitude, _nullishCoalesce(base.waveAmplitude, () => ( 0))),
|
|
1090
|
+
waveLength: typeof raw.waveLength === "number" && Number.isFinite(raw.waveLength) ? Math.min(2e3, Math.max(4, raw.waveLength)) : _nullishCoalesce(base.waveLength, () => ( DEFAULT_PARTICLE.waveLength)),
|
|
1091
|
+
waveSpeed: unit3(raw.waveSpeed, _nullishCoalesce(base.waveSpeed, () => ( 0))),
|
|
1092
|
+
wavePhaseJitter: unit3(raw.wavePhaseJitter, _nullishCoalesce(base.wavePhaseJitter, () => ( 0))),
|
|
1093
|
+
dotTextures: textures,
|
|
1094
|
+
dotTextureOrder: oneOf(raw.dotTextureOrder, DOT_TEXTURE_ORDERS, _nullishCoalesce(base.dotTextureOrder, () => ( DEFAULT_PARTICLE.dotTextureOrder))),
|
|
1095
|
+
dotRotation: oneOf(raw.dotRotation, DOT_ROTATIONS, _nullishCoalesce(base.dotRotation, () => ( DEFAULT_PARTICLE.dotRotation)))
|
|
1013
1096
|
};
|
|
1014
1097
|
}
|
|
1098
|
+
var FUZZY_AMPLITUDE = 0.12;
|
|
1099
|
+
var NOMINAL_MS_PER_PX = 2;
|
|
1100
|
+
var RIBBON_SUB = 4;
|
|
1101
|
+
function chainDamp(damp, k) {
|
|
1102
|
+
return Math.min(damp, 1 / (1 + 0.8 * Math.sqrt(Math.max(0, k))) ** 2);
|
|
1103
|
+
}
|
|
1104
|
+
function addPolygonArea(pts, rect, out, weight = 1) {
|
|
1105
|
+
const n = pts.length >> 1;
|
|
1106
|
+
if (n < 3) return;
|
|
1107
|
+
let y0 = Infinity;
|
|
1108
|
+
let y1 = -Infinity;
|
|
1109
|
+
for (let i = 1; i < pts.length; i += 2) {
|
|
1110
|
+
if (pts[i] < y0) y0 = pts[i];
|
|
1111
|
+
if (pts[i] > y1) y1 = pts[i];
|
|
1112
|
+
}
|
|
1113
|
+
const r0 = Math.max(0, Math.floor(y0) - rect.y);
|
|
1114
|
+
const r1 = Math.min(rect.height - 1, Math.floor(y1) - rect.y);
|
|
1115
|
+
const xs = [];
|
|
1116
|
+
const w = weight / RIBBON_SUB;
|
|
1117
|
+
for (let row = r0; row <= r1; row++) {
|
|
1118
|
+
const base = row * rect.width;
|
|
1119
|
+
for (let sub = 0; sub < RIBBON_SUB; sub++) {
|
|
1120
|
+
const y = rect.y + row + (sub + 0.5) / RIBBON_SUB;
|
|
1121
|
+
xs.length = 0;
|
|
1122
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
1123
|
+
const yi = pts[i * 2 + 1];
|
|
1124
|
+
const yj = pts[j * 2 + 1];
|
|
1125
|
+
if (yi <= y !== yj <= y) xs.push(pts[i * 2] + (y - yi) / (yj - yi) * (pts[j * 2] - pts[i * 2]));
|
|
1126
|
+
}
|
|
1127
|
+
if (xs.length < 2) continue;
|
|
1128
|
+
xs.sort((a, b) => a - b);
|
|
1129
|
+
for (let k = 0; k + 1 < xs.length; k += 2) {
|
|
1130
|
+
const a = Math.max(rect.x, xs[k]);
|
|
1131
|
+
const b = Math.min(rect.x + rect.width, xs[k + 1]);
|
|
1132
|
+
if (!(b > a)) continue;
|
|
1133
|
+
for (let px = Math.floor(a); px < b; px++) {
|
|
1134
|
+
const cover = Math.min(b, px + 1) - Math.max(a, px);
|
|
1135
|
+
if (cover > 0) out[base + px - rect.x] += cover * w;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1015
1141
|
function curl(x, y, seed) {
|
|
1016
1142
|
const n = (u, v) => {
|
|
1017
1143
|
const x0 = Math.floor(u);
|
|
@@ -1032,12 +1158,51 @@ function curl(x, y, seed) {
|
|
|
1032
1158
|
}
|
|
1033
1159
|
var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
1034
1160
|
|
|
1161
|
+
/** Ribbon pairs per copy: particles in their order around the pen (non-flow kinds). */
|
|
1162
|
+
|
|
1035
1163
|
|
|
1036
1164
|
__init30() {this.spawn = 0}
|
|
1165
|
+
/** Textured dots drawn so far, per copy (sequence order, random picks). */
|
|
1166
|
+
|
|
1167
|
+
/** Ribbon fill: the area each pixel has had so far this stroke (telescoped into the coverage). */
|
|
1168
|
+
__init31() {this.filled = /* @__PURE__ */ new Map()}
|
|
1169
|
+
__init32() {this.pts = []}
|
|
1037
1170
|
constructor(brush, options) {
|
|
1038
|
-
super(brush, options);_class6.prototype.__init30.call(this);;
|
|
1171
|
+
super(brush, options);_class6.prototype.__init30.call(this);_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);;
|
|
1039
1172
|
this.s = brush.particle;
|
|
1040
1173
|
this.sets = this.copies.map(() => []);
|
|
1174
|
+
this.orders = this.copies.map(() => []);
|
|
1175
|
+
this.dots = this.copies.map(() => 0);
|
|
1176
|
+
}
|
|
1177
|
+
/** Strand kinds: a chain of masses behind each particle. */
|
|
1178
|
+
get strands() {
|
|
1179
|
+
return this.s.kind === "string" || this.s.kind === "elastic" || this.s.kind === "fuzzy";
|
|
1180
|
+
}
|
|
1181
|
+
newParticle(x, y, ox, oy, k, phase, i, copy) {
|
|
1182
|
+
const s = this.s;
|
|
1183
|
+
const fuzzy = s.kind === "fuzzy";
|
|
1184
|
+
const jitter = fuzzy ? 1 : s.wavePhaseJitter;
|
|
1185
|
+
return {
|
|
1186
|
+
x,
|
|
1187
|
+
y,
|
|
1188
|
+
vx: 0,
|
|
1189
|
+
vy: 0,
|
|
1190
|
+
ox,
|
|
1191
|
+
oy,
|
|
1192
|
+
k,
|
|
1193
|
+
phase,
|
|
1194
|
+
age: 0,
|
|
1195
|
+
run: 0,
|
|
1196
|
+
bead: 0,
|
|
1197
|
+
alive: true,
|
|
1198
|
+
wavePhase: jitter > 0 ? jitter * Math.PI * 2 * _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 43 + copy) : 0,
|
|
1199
|
+
waveScale: fuzzy ? 0.6 + 0.8 * _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 47 + copy) : 1,
|
|
1200
|
+
dx: x,
|
|
1201
|
+
dy: y,
|
|
1202
|
+
px: NaN,
|
|
1203
|
+
py: NaN,
|
|
1204
|
+
fade: 1
|
|
1205
|
+
};
|
|
1041
1206
|
}
|
|
1042
1207
|
init(copy, d) {
|
|
1043
1208
|
const s = this.s;
|
|
@@ -1048,18 +1213,28 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
|
1048
1213
|
const a = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 5 + copy) * Math.PI * 2;
|
|
1049
1214
|
const ox = Math.cos(a) * r;
|
|
1050
1215
|
const oy = Math.sin(a) * r;
|
|
1051
|
-
const p =
|
|
1052
|
-
if (
|
|
1216
|
+
const p = this.newParticle(d.x + ox, d.y + oy, ox, oy, 0.6 + 0.8 * _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 7), a, i, copy);
|
|
1217
|
+
if (this.strands) p.strand = Array.from({ length: 6 }, () => ({ x: d.x + ox, y: d.y + oy, vx: 0, vy: 0 }));
|
|
1053
1218
|
if (s.kind === "orbit") {
|
|
1054
1219
|
p.ox = Math.max(1, r + size * 0.15);
|
|
1055
1220
|
p.oy = 0;
|
|
1056
1221
|
}
|
|
1057
1222
|
list.push(p);
|
|
1058
1223
|
}
|
|
1224
|
+
this.orders[copy] = list.map((_, i) => i).sort((i, j) => list[i].phase - list[j].phase || i - j);
|
|
1225
|
+
}
|
|
1226
|
+
/** The sideways wave of particle `p` at pen dab `d` (px along the normal to the pen's direction). */
|
|
1227
|
+
wave(p, d, size) {
|
|
1228
|
+
const s = this.s;
|
|
1229
|
+
const amp = s.kind === "fuzzy" ? Math.max(FUZZY_AMPLITUDE, s.waveAmplitude) : s.waveAmplitude;
|
|
1230
|
+
if (!(amp > 0)) return 0;
|
|
1231
|
+
const ms = _nullishCoalesce(d.time, () => ( d.travel * NOMINAL_MS_PER_PX));
|
|
1232
|
+
const phase = 2 * Math.PI * d.travel / (s.waveLength * p.waveScale) - s.waveSpeed * 0.025 * ms + p.wavePhase;
|
|
1233
|
+
return amp * size * Math.sin(phase);
|
|
1059
1234
|
}
|
|
1060
1235
|
advance(copy, d, rects) {
|
|
1061
1236
|
const s = this.s;
|
|
1062
|
-
|
|
1237
|
+
let list = this.sets[copy];
|
|
1063
1238
|
if (d.index === 0 && !list.length) this.init(copy, d);
|
|
1064
1239
|
const size = Math.max(1, d.radius * 2);
|
|
1065
1240
|
const markR = Math.max(0.5, size * s.size / 2);
|
|
@@ -1068,6 +1243,8 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
|
1068
1243
|
const g = s.gravity * 0.15;
|
|
1069
1244
|
const op = d.opacity;
|
|
1070
1245
|
const al = Math.max(0.05, d.alpha);
|
|
1246
|
+
const nx = Math.sin(d.direction);
|
|
1247
|
+
const ny = Math.cos(d.direction);
|
|
1071
1248
|
if (s.kind === "flow") {
|
|
1072
1249
|
this.spawn += s.count / 8;
|
|
1073
1250
|
while (this.spawn >= 1) {
|
|
@@ -1075,15 +1252,13 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
|
1075
1252
|
const i = this.dabCount + list.length * 31 + Math.floor(d.index);
|
|
1076
1253
|
const a = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 11 + copy) * Math.PI * 2;
|
|
1077
1254
|
const r = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 13 + copy) * s.spread * size * 0.5;
|
|
1078
|
-
list.push(
|
|
1255
|
+
list.push(this.newParticle(d.x + Math.cos(a) * r, d.y + Math.sin(a) * r, 0, 0, 1, a, i, copy));
|
|
1079
1256
|
}
|
|
1080
1257
|
}
|
|
1081
1258
|
const lifeSteps = 8 + 120 * s.life;
|
|
1082
1259
|
for (let i = 0; i < list.length; i++) {
|
|
1083
1260
|
const p = list[i];
|
|
1084
1261
|
if (!p.alive) continue;
|
|
1085
|
-
const px = p.x;
|
|
1086
|
-
const py = p.y;
|
|
1087
1262
|
switch (s.kind) {
|
|
1088
1263
|
case "spring": {
|
|
1089
1264
|
p.vx = (p.vx + (d.x + p.ox - p.x) * k * p.k) * damp;
|
|
@@ -1092,13 +1267,38 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
|
1092
1267
|
p.y += p.vy;
|
|
1093
1268
|
break;
|
|
1094
1269
|
}
|
|
1095
|
-
case "string":
|
|
1270
|
+
case "string":
|
|
1271
|
+
case "fuzzy": {
|
|
1272
|
+
const strand = p.strand;
|
|
1273
|
+
const dm = s.kind === "fuzzy" ? chainDamp(damp, k * p.k) : damp;
|
|
1274
|
+
let ax = d.x + p.ox;
|
|
1275
|
+
let ay = d.y + p.oy;
|
|
1276
|
+
for (const m of strand) {
|
|
1277
|
+
m.vx = (m.vx + (ax - m.x) * k * p.k + 0) * dm;
|
|
1278
|
+
m.vy = (m.vy + (ay - m.y) * k * p.k + g) * dm;
|
|
1279
|
+
m.x += m.vx;
|
|
1280
|
+
m.y += m.vy;
|
|
1281
|
+
ax = m.x;
|
|
1282
|
+
ay = m.y;
|
|
1283
|
+
}
|
|
1284
|
+
p.x = strand[strand.length - 1].x;
|
|
1285
|
+
p.y = strand[strand.length - 1].y;
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1288
|
+
case "elastic": {
|
|
1096
1289
|
const strand = p.strand;
|
|
1290
|
+
const rest = size * 0.12;
|
|
1291
|
+
const ke = Math.min(0.9, (0.1 + 0.6 * s.stiffness) * p.k);
|
|
1292
|
+
const dm = chainDamp(damp, ke);
|
|
1097
1293
|
let ax = d.x + p.ox;
|
|
1098
1294
|
let ay = d.y + p.oy;
|
|
1099
1295
|
for (const m of strand) {
|
|
1100
|
-
|
|
1101
|
-
|
|
1296
|
+
const ex = ax - m.x;
|
|
1297
|
+
const ey = ay - m.y;
|
|
1298
|
+
const len = Math.hypot(ex, ey);
|
|
1299
|
+
const pull = len > rest ? (len - rest) / len : 0;
|
|
1300
|
+
m.vx = (m.vx + ex * pull * ke) * dm;
|
|
1301
|
+
m.vy = (m.vy + ey * pull * ke + g) * dm;
|
|
1102
1302
|
m.x += m.vx;
|
|
1103
1303
|
m.y += m.vy;
|
|
1104
1304
|
ax = m.x;
|
|
@@ -1127,11 +1327,21 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
|
1127
1327
|
break;
|
|
1128
1328
|
}
|
|
1129
1329
|
}
|
|
1330
|
+
const w = this.wave(p, d, size);
|
|
1331
|
+
const qx = w ? p.x + nx * w : p.x;
|
|
1332
|
+
const qy = w ? p.y + ny * w : p.y;
|
|
1333
|
+
const px = p.dx;
|
|
1334
|
+
const py = p.dy;
|
|
1335
|
+
p.px = p.dx;
|
|
1336
|
+
p.py = p.dy;
|
|
1337
|
+
p.dx = qx;
|
|
1338
|
+
p.dy = qy;
|
|
1130
1339
|
const fade = s.kind === "flow" ? 1 - p.age / lifeSteps : 1;
|
|
1131
|
-
|
|
1340
|
+
p.fade = fade;
|
|
1341
|
+
const moved = Math.hypot(qx - px, qy - py);
|
|
1132
1342
|
if (d.index === 0 && s.kind !== "flow") continue;
|
|
1133
|
-
if (s.render === "dot") this.
|
|
1134
|
-
else if (s.render === "line") this.segment(px, py,
|
|
1343
|
+
if (s.render === "dot") this.dot(copy, qx, qy, qx - px, qy - py, markR * fade, al * fade, op, d, rects);
|
|
1344
|
+
else if (s.render === "line" || s.render === "ribbon") this.segment(px, py, qx, qy, markR * fade, al * fade, op, d.pressure, rects);
|
|
1135
1345
|
else {
|
|
1136
1346
|
p.run += moved;
|
|
1137
1347
|
const gap = Math.max(markR * 2, size * (0.05 + s.interval));
|
|
@@ -1139,13 +1349,101 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
|
|
|
1139
1349
|
p.run -= gap;
|
|
1140
1350
|
p.bead++;
|
|
1141
1351
|
const t = moved > 0 ? 1 - p.run / Math.max(moved, 1e-6) : 1;
|
|
1142
|
-
const bx = px + (
|
|
1143
|
-
const by = py + (
|
|
1144
|
-
this.
|
|
1352
|
+
const bx = px + (qx - px) * Math.min(1, Math.max(0, t));
|
|
1353
|
+
const by = py + (qy - py) * Math.min(1, Math.max(0, t));
|
|
1354
|
+
this.dot(copy, bx, by, qx - px, qy - py, markR * (0.7 + 0.6 * (p.bead % 3 / 2)) * fade, al * fade, op, d, rects);
|
|
1145
1355
|
}
|
|
1146
1356
|
}
|
|
1147
1357
|
}
|
|
1148
|
-
if (s.
|
|
1358
|
+
if (s.render === "ribbon" && !(d.index === 0 && s.kind !== "flow")) this.ribbon(copy, list, op, al, d, rects);
|
|
1359
|
+
if (s.kind === "flow" && list.length > 512) {
|
|
1360
|
+
list = list.filter((p) => p.alive);
|
|
1361
|
+
this.sets[copy] = list;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
/** A dot (or bead): round, or a textured stamp turned by the particle's motion (mx, my), at random or to the tip angle. */
|
|
1365
|
+
dot(copy, x, y, mx, my, r, alpha, opacity, d, rects) {
|
|
1366
|
+
const s = this.s;
|
|
1367
|
+
const textures = s.dotTextures;
|
|
1368
|
+
if (!textures.length) return this.mark(x, y, r, alpha, opacity, d.pressure, rects);
|
|
1369
|
+
const n = this.dots[copy]++;
|
|
1370
|
+
const pick = s.dotTextureOrder === "random" ? Math.min(textures.length - 1, Math.floor(_chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, n, 31 + copy) * textures.length)) : n % textures.length;
|
|
1371
|
+
const rotation = s.dotRotation === "random" ? _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, n, 37 + copy) * Math.PI * 2 : s.dotRotation === "follow" ? mx || my ? Math.atan2(my, mx) : -d.direction : -this.brush.tip.angle * Math.PI / 180;
|
|
1372
|
+
this.stamp(x, y, r, textures[pick], rotation, this.seed + n * 2654435761 >>> 0, alpha, opacity, d.pressure, rects);
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Ribbon fill for one step: the band between each pair of joined particles, from where they
|
|
1376
|
+
* were drawn last step to where they are drawn now. Bands of one step, and of consecutive steps,
|
|
1377
|
+
* share edges exactly, so the fill has no seams; it is telescoped into the coverage
|
|
1378
|
+
* (`telescope`), so it does not build up at the joins.
|
|
1379
|
+
*/
|
|
1380
|
+
ribbon(copy, list, opacity, alpha, d, rects) {
|
|
1381
|
+
const s = this.s;
|
|
1382
|
+
if (!(s.ribbonFill > 0)) return;
|
|
1383
|
+
const live = (p) => !!p && p.alive && !Number.isNaN(p.px);
|
|
1384
|
+
let order;
|
|
1385
|
+
if (s.kind === "flow") order = list.filter(live);
|
|
1386
|
+
else order = this.orders[copy].map((i) => list[i]).filter(live);
|
|
1387
|
+
if (order.length < 2) return;
|
|
1388
|
+
const pairs = [];
|
|
1389
|
+
if (s.ribbonPairs === "first-last") pairs.push([order[0], order[order.length - 1]]);
|
|
1390
|
+
else for (let i = 0; i + 1 < order.length; i++) pairs.push([order[i], order[i + 1]]);
|
|
1391
|
+
const pts = this.pts;
|
|
1392
|
+
const ceil = Math.min(1, opacity * s.ribbonFill);
|
|
1393
|
+
for (const [a, b] of pairs) {
|
|
1394
|
+
pts.length = 0;
|
|
1395
|
+
pts.push(a.px, a.py, a.dx, a.dy, b.dx, b.dy, b.px, b.py);
|
|
1396
|
+
let x0 = Infinity;
|
|
1397
|
+
let y0 = Infinity;
|
|
1398
|
+
let x1 = -Infinity;
|
|
1399
|
+
let y1 = -Infinity;
|
|
1400
|
+
for (let i = 0; i < 8; i += 2) {
|
|
1401
|
+
x0 = Math.min(x0, pts[i]);
|
|
1402
|
+
x1 = Math.max(x1, pts[i]);
|
|
1403
|
+
y0 = Math.min(y0, pts[i + 1]);
|
|
1404
|
+
y1 = Math.max(y1, pts[i + 1]);
|
|
1405
|
+
}
|
|
1406
|
+
const rect = { x: Math.floor(x0), y: Math.floor(y0), width: Math.floor(x1) - Math.floor(x0) + 1, height: Math.floor(y1) - Math.floor(y0) + 1 };
|
|
1407
|
+
const area = this.scratch(rect.width * rect.height);
|
|
1408
|
+
addPolygonArea(pts, rect, area, Math.min(a.fade, b.fade) * alpha);
|
|
1409
|
+
if (this.telescope(rect, area)) this.lay(rect, area, ceil, d.pressure, (a.dx + b.dx) / 2, (a.dy + b.dy) / 2, rects);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Turns the area newly filled over `rect` into coverage to lay towards the fill's ceiling: the
|
|
1414
|
+
* share of what was left unfilled, so after any number of bands (in any grouping) a pixel's
|
|
1415
|
+
* coverage is the ceiling times the area filled there. False when nothing was added.
|
|
1416
|
+
*/
|
|
1417
|
+
telescope(rect, area) {
|
|
1418
|
+
let any = false;
|
|
1419
|
+
for (let yy = 0, k = 0; yy < rect.height; yy++) {
|
|
1420
|
+
const gy = rect.y + yy;
|
|
1421
|
+
const ty = Math.floor(gy / _chunk474IQG74cjs.TILE);
|
|
1422
|
+
let t;
|
|
1423
|
+
let tx = NaN;
|
|
1424
|
+
for (let xx = 0; xx < rect.width; xx++, k++) {
|
|
1425
|
+
const add = area[k];
|
|
1426
|
+
if (!(add > 0)) continue;
|
|
1427
|
+
const gx = rect.x + xx;
|
|
1428
|
+
const ntx = Math.floor(gx / _chunk474IQG74cjs.TILE);
|
|
1429
|
+
if (ntx !== tx || !t) {
|
|
1430
|
+
tx = ntx;
|
|
1431
|
+
const key = _chunk474IQG74cjs.tileKey.call(void 0, tx, ty);
|
|
1432
|
+
t = this.filled.get(key);
|
|
1433
|
+
if (!t) {
|
|
1434
|
+
t = new Float32Array(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE);
|
|
1435
|
+
this.filled.set(key, t);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
const ti = (gy - ty * _chunk474IQG74cjs.TILE) * _chunk474IQG74cjs.TILE + (gx - tx * _chunk474IQG74cjs.TILE);
|
|
1439
|
+
const before = t[ti];
|
|
1440
|
+
const after = Math.min(1, before + add);
|
|
1441
|
+
t[ti] = after;
|
|
1442
|
+
area[k] = before < 1 ? (after - before) / (1 - before) : 0;
|
|
1443
|
+
any = true;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return any;
|
|
1149
1447
|
}
|
|
1150
1448
|
}, _class6);
|
|
1151
1449
|
var DEFAULT_BRISTLE = { count: 32, stiffness: 0.5, splay: 0.5, thickness: 0.06, load: 0.6, jitter: 0.4 };
|
|
@@ -1897,6 +2195,66 @@ var BUILTIN_BRUSHES = [
|
|
|
1897
2195
|
particle: { kind: "flow", render: "dot", count: 8, size: 0.06, spread: 0.7, speed: 0.9, life: 0.3 },
|
|
1898
2196
|
light: { amount: 1 }
|
|
1899
2197
|
}),
|
|
2198
|
+
builtin({
|
|
2199
|
+
// Silk of light: strands dragged behind the pen on slow waves, the bands between them filled.
|
|
2200
|
+
id: "light-ribbon",
|
|
2201
|
+
name: "Light ribbon",
|
|
2202
|
+
category: "light",
|
|
2203
|
+
engine: "particle",
|
|
2204
|
+
tip: { size: 80, hardness: 0.8, opacity: 0.9, flow: 1, smoothing: 0.35, pressureSize: false, pressureOpacity: false },
|
|
2205
|
+
particle: { kind: "string", render: "ribbon", count: 5, size: 0.015, spread: 1, stiffness: 0.05, damping: 0.3, ribbonFill: 0.45, waveAmplitude: 0.25, waveLength: 220, wavePhaseJitter: 0.25 },
|
|
2206
|
+
light: { amount: 0.8 }
|
|
2207
|
+
}),
|
|
2208
|
+
builtin({
|
|
2209
|
+
// Flames: short-lived bands thrown off the pen, rising and curling in the flow.
|
|
2210
|
+
id: "flame-ribbon",
|
|
2211
|
+
name: "Flame ribbon",
|
|
2212
|
+
category: "light",
|
|
2213
|
+
engine: "particle",
|
|
2214
|
+
tip: { size: 50, hardness: 0.6, opacity: 0.85, flow: 1, smoothing: 0.25, pressureSize: true, pressureOpacity: false },
|
|
2215
|
+
particle: { kind: "flow", render: "ribbon", count: 4, size: 0.012, spread: 0.5, speed: 0.6, gravity: -0.6, life: 0.2, ribbonFill: 0.5 },
|
|
2216
|
+
light: { amount: 1 }
|
|
2217
|
+
}),
|
|
2218
|
+
builtin({
|
|
2219
|
+
// A neon tube that lags and snaps back, with a slow shimmer.
|
|
2220
|
+
id: "neon-strand",
|
|
2221
|
+
name: "Neon strand",
|
|
2222
|
+
category: "light",
|
|
2223
|
+
engine: "particle",
|
|
2224
|
+
tip: { size: 40, hardness: 0.9, opacity: 1, flow: 1, smoothing: 0.4, pressureSize: false, pressureOpacity: false },
|
|
2225
|
+
particle: { kind: "elastic", render: "line", count: 3, size: 0.05, spread: 0.25, stiffness: 0.6, damping: 0.3, waveAmplitude: 0.06, waveLength: 120 },
|
|
2226
|
+
light: { amount: 1 }
|
|
2227
|
+
}),
|
|
2228
|
+
builtin({
|
|
2229
|
+
// Smoke lit from within: thin bands thrown into a slow, rising flow.
|
|
2230
|
+
id: "smoke-ribbon",
|
|
2231
|
+
name: "Smoke ribbon",
|
|
2232
|
+
category: "light",
|
|
2233
|
+
engine: "particle",
|
|
2234
|
+
tip: { size: 70, hardness: 0.3, opacity: 0.45, flow: 1, smoothing: 0.3, pressureSize: true, pressureOpacity: false },
|
|
2235
|
+
particle: { kind: "flow", render: "ribbon", count: 3, size: 5e-3, spread: 0.5, speed: 0.15, gravity: -0.15, life: 0.35, ribbonFill: 0.25, waveAmplitude: 0.1, waveLength: 140, wavePhaseJitter: 1 },
|
|
2236
|
+
light: { amount: 0.3 }
|
|
2237
|
+
}),
|
|
2238
|
+
builtin({
|
|
2239
|
+
// Out-of-focus lights: soft discs and rings spaced along springy paths.
|
|
2240
|
+
id: "bokeh-dots",
|
|
2241
|
+
name: "Bokeh dots",
|
|
2242
|
+
category: "light",
|
|
2243
|
+
engine: "particle",
|
|
2244
|
+
tip: { size: 80, hardness: 0.7, opacity: 0.6, flow: 1, smoothing: 0.3, pressureSize: true, pressureOpacity: false },
|
|
2245
|
+
particle: { kind: "spring", render: "sequence", count: 3, size: 0.3, spread: 1, stiffness: 0.15, damping: 0.4, interval: 1, dotTextures: ["dot", "dot", "ring"], dotTextureOrder: "random" },
|
|
2246
|
+
light: { amount: 0.9 }
|
|
2247
|
+
}),
|
|
2248
|
+
builtin({
|
|
2249
|
+
// Sparks: twinkles and stars thrown into the flow, spaced along it.
|
|
2250
|
+
id: "twinkle-sparks",
|
|
2251
|
+
name: "Twinkle sparks",
|
|
2252
|
+
category: "light",
|
|
2253
|
+
engine: "particle",
|
|
2254
|
+
tip: { size: 50, hardness: 0.8, opacity: 1, flow: 1, smoothing: 0.1, pressureSize: true, pressureOpacity: false },
|
|
2255
|
+
particle: { kind: "flow", render: "sequence", count: 3, size: 0.25, spread: 0.8, speed: 0.9, life: 0.35, interval: 0.6, dotTextures: ["sparkle", "sparkle", "star"], dotTextureOrder: "random", dotRotation: "random" },
|
|
2256
|
+
light: { amount: 1 }
|
|
2257
|
+
}),
|
|
1900
2258
|
builtin({
|
|
1901
2259
|
id: "glow-wash",
|
|
1902
2260
|
name: "Glow wash",
|
|
@@ -1908,6 +2266,16 @@ var BUILTIN_BRUSHES = [
|
|
|
1908
2266
|
wet: { tool: "paint", water: 0.65, pigment: 0.75, body: 0, rewet: 0.1 },
|
|
1909
2267
|
light: { amount: 0.7 }
|
|
1910
2268
|
}),
|
|
2269
|
+
builtin({
|
|
2270
|
+
// Fluorescent ink: a transparent chisel marker that multiplies (text under it stays dark) and
|
|
2271
|
+
// glows a little brighter than the paper. Turn the layer's spill off (set_light_spill radius 0).
|
|
2272
|
+
id: "highlighter",
|
|
2273
|
+
name: "Highlighter",
|
|
2274
|
+
category: "light",
|
|
2275
|
+
tip: { size: 30, hardness: 0.85, spacing: 0.04, angle: 45, roundness: 0.35, opacity: 0.6, flow: 1, smoothing: 0.3, pressureSize: false, pressureOpacity: false, blend: "multiply" },
|
|
2276
|
+
grain: 0.05,
|
|
2277
|
+
light: { amount: 0.5 }
|
|
2278
|
+
}),
|
|
1911
2279
|
builtin({
|
|
1912
2280
|
id: "sticker-stars",
|
|
1913
2281
|
name: "Star stickers",
|
|
@@ -2107,4 +2475,9 @@ function parseBrushLibrary(raw, problems = []) {
|
|
|
2107
2475
|
|
|
2108
2476
|
|
|
2109
2477
|
|
|
2110
|
-
|
|
2478
|
+
|
|
2479
|
+
|
|
2480
|
+
|
|
2481
|
+
|
|
2482
|
+
|
|
2483
|
+
exports.EFFECT_STRIP_HEIGHT = EFFECT_STRIP_HEIGHT; exports.EFFECT_MAP_REPEATS = EFFECT_MAP_REPEATS; exports.effectPaintSize = effectPaintSize; exports.readEffectStrokeMap = readEffectStrokeMap; exports.effectPaintKey = effectPaintKey; exports.isPaintEffectPaint = isPaintEffectPaint; exports.readPaintEffectPaint = readPaintEffectPaint; exports.sampleEffectField = sampleEffectField; exports.sampleEffectAt = sampleEffectAt; exports.effectDabField = effectDabField; exports.PAPER_FAMILIES = PAPER_FAMILIES; exports.PAPER_LIBRARY = PAPER_LIBRARY; exports.DEFAULT_PAPER = DEFAULT_PAPER; exports.paperPreset = paperPreset; exports.clonePaper = clonePaper; exports.normalizePaper = normalizePaper; exports.hexToRgb8 = hexToRgb8; exports.renderPaper = renderPaper; exports.penOrientation = penOrientation; exports.tiltToOrientation = tiltToOrientation; exports.PenSmoother = PenSmoother; exports.IDENTITY_COPY = IDENTITY_COPY; exports.PenDabWalker = PenDabWalker; exports.DryStroke = DryStroke; exports.mixerTip = mixerTip; exports.toStrokePoint = toStrokePoint; exports.STAMP_KINDS = STAMP_KINDS; exports.DEFAULT_STICKER = DEFAULT_STICKER; exports.normalizeStickerSettings = normalizeStickerSettings; exports.stampCoverage = stampCoverage; exports.stickerRect = stickerRect; exports.drawSticker = drawSticker; exports.StickerSpray = StickerSpray; exports.renderStickers = renderStickers; exports.normalizeStickerUpdate = normalizeStickerUpdate; exports.stickerSurface = stickerSurface; exports.stickerAt = stickerAt; 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.addPolygonArea = addPolygonArea; exports.ParticleStroke = ParticleStroke; exports.DEFAULT_BRISTLE = DEFAULT_BRISTLE; exports.normalizeBristle = normalizeBristle; exports.BristleStroke = BristleStroke; 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.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.DEFAULT_DYNAMICS = DEFAULT_DYNAMICS; exports.normalizeDynamics = normalizeDynamics; exports.BRUSH_ID = BRUSH_ID; exports.normalizeMultiColor = normalizeMultiColor; exports.normalizeBehavior = normalizeBehavior; exports.isWetCapable = isWetCapable; exports.isWetBrush = isWetBrush; exports.usesPaintExchange = usesPaintExchange; exports.brushTakesPaint = brushTakesPaint; exports.paintToMultiColor = paintToMultiColor; exports.multiColorToPaint = multiColorToPaint; exports.normalizePaintBrush = normalizePaintBrush; exports.BUILTIN_BRUSHES = BUILTIN_BRUSHES; exports.DEFAULT_BRUSH_ID = DEFAULT_BRUSH_ID; exports.brushToJSON = brushToJSON; exports.parseBrushLibrary = parseBrushLibrary;
|