@pixerate/editor 0.135.0 → 0.137.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 +63 -55
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +8 -5
- package/dist/animation-editor/index.d.ts +8 -5
- package/dist/animation-editor/index.js +10 -2
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/{bezier-J8i4uqSH.d.cts → bezier-CZ2AhjID.d.cts} +1 -1
- package/dist/{bezier-lNEOp0Jx.d.ts → bezier-Dywv3Bw8.d.ts} +1 -1
- package/dist/blending-D7jCmjO6.d.cts +1026 -0
- package/dist/blending-NgF3qo3d.d.ts +1026 -0
- package/dist/brushes-CnvZSYYU.d.cts +744 -0
- package/dist/brushes-DJmUbIgz.d.ts +744 -0
- package/dist/{brushstrokeRender-SII6MG4R.js → brushstrokeRender-2FAXA3XC.js} +13 -16
- package/dist/{brushstrokeRender-SII6MG4R.js.map → brushstrokeRender-2FAXA3XC.js.map} +1 -1
- package/dist/{brushstrokeRender-JNHVEUFG.cjs → brushstrokeRender-IMFY3FF6.cjs} +16 -19
- package/dist/brushstrokeRender-IMFY3FF6.cjs.map +1 -0
- package/dist/canvas/index.cjs +10 -10
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +10 -7
- package/dist/canvas/index.d.ts +10 -7
- package/dist/canvas/index.js +4 -4
- package/dist/{chunk-W4LNKTHT.cjs → chunk-2XS6LTUP.cjs} +63 -63
- package/dist/{chunk-W4LNKTHT.cjs.map → chunk-2XS6LTUP.cjs.map} +1 -1
- package/dist/{chunk-HHTCDFZC.js → chunk-3D77LOBM.js} +234 -136
- package/dist/chunk-3D77LOBM.js.map +1 -0
- package/dist/{chunk-BR7EKQUP.js → chunk-3G6J7UFV.js} +322 -2
- package/dist/chunk-3G6J7UFV.js.map +1 -0
- package/dist/{chunk-62XUO5LB.js → chunk-3X6IAJUQ.js} +2 -2
- package/dist/{chunk-EM3FMJWE.js → chunk-5FSRTP6E.js} +320 -3
- package/dist/chunk-5FSRTP6E.js.map +1 -0
- package/dist/{chunk-Z2H7KECK.cjs → chunk-5OOIUXNJ.cjs} +345 -247
- package/dist/chunk-5OOIUXNJ.cjs.map +1 -0
- package/dist/{chunk-7RJ6P7MI.js → chunk-6S6362D2.js} +2 -2
- package/dist/chunk-A3BVTI3G.js +150 -0
- package/dist/chunk-A3BVTI3G.js.map +1 -0
- package/dist/{chunk-24ATT2PE.cjs → chunk-AEAAN4AD.cjs} +120 -120
- package/dist/{chunk-24ATT2PE.cjs.map → chunk-AEAAN4AD.cjs.map} +1 -1
- package/dist/chunk-ECCXGELC.cjs +727 -0
- package/dist/chunk-ECCXGELC.cjs.map +1 -0
- package/dist/{chunk-SVYE5HIM.js → chunk-EQQMGO42.js} +2 -2
- package/dist/{chunk-FHPVDTI2.cjs → chunk-F2Y4E4GU.cjs} +22 -22
- package/dist/{chunk-FHPVDTI2.cjs.map → chunk-F2Y4E4GU.cjs.map} +1 -1
- package/dist/{chunk-OXSV64JX.js → chunk-G6IKY6PL.js} +4 -4
- package/dist/{chunk-UYQUPUR6.js → chunk-HUO3TI4C.js} +3 -3
- package/dist/chunk-I52DRGGJ.js +727 -0
- package/dist/chunk-I52DRGGJ.js.map +1 -0
- package/dist/{chunk-DDSO4HS7.cjs → chunk-KEENUAFN.cjs} +2 -2
- package/dist/{chunk-DDSO4HS7.cjs.map → chunk-KEENUAFN.cjs.map} +1 -1
- package/dist/{chunk-2HC4DRF4.js → chunk-LLFHCSU2.js} +2 -2
- package/dist/{chunk-J5BA3BQ6.cjs → chunk-O7LUD7MH.cjs} +323 -3
- package/dist/chunk-O7LUD7MH.cjs.map +1 -0
- package/dist/{chunk-ILYFOJUN.js → chunk-OIBIRXRG.js} +3 -3
- package/dist/{chunk-FWEO3HSL.js → chunk-PQQF5KOW.js} +8 -8
- package/dist/{chunk-T6TV6HY6.cjs → chunk-PRSRYHQD.cjs} +36 -36
- package/dist/{chunk-T6TV6HY6.cjs.map → chunk-PRSRYHQD.cjs.map} +1 -1
- package/dist/chunk-Q6OUXEK3.cjs +150 -0
- package/dist/chunk-Q6OUXEK3.cjs.map +1 -0
- package/dist/{chunk-6GFRKLLS.cjs → chunk-QBWEH3IM.cjs} +429 -112
- package/dist/chunk-QBWEH3IM.cjs.map +1 -0
- package/dist/{chunk-ESIWZMMK.js → chunk-SYT2TLQF.js} +35 -37
- package/dist/{chunk-ESIWZMMK.js.map → chunk-SYT2TLQF.js.map} +1 -1
- package/dist/{chunk-YT3JL7YM.cjs → chunk-TLCTTQFF.cjs} +53 -53
- package/dist/{chunk-YT3JL7YM.cjs.map → chunk-TLCTTQFF.cjs.map} +1 -1
- package/dist/{chunk-P3RQ5UJE.cjs → chunk-UTQKC66X.cjs} +4 -4
- package/dist/{chunk-P3RQ5UJE.cjs.map → chunk-UTQKC66X.cjs.map} +1 -1
- package/dist/{chunk-J3B45HTA.cjs → chunk-XB25WOC2.cjs} +6 -6
- package/dist/{chunk-J3B45HTA.cjs.map → chunk-XB25WOC2.cjs.map} +1 -1
- package/dist/{chunk-YAFS2QFZ.cjs → chunk-XN2NBLV5.cjs} +313 -315
- package/dist/chunk-XN2NBLV5.cjs.map +1 -0
- package/dist/effects/index.d.cts +7 -4
- package/dist/effects/index.d.ts +7 -4
- package/dist/{export-DMeG6E06.d.ts → export-41rL6Cne.d.ts} +1 -1
- package/dist/{export-AZSwX087.d.cts → export-CyJdfP_Z.d.cts} +5 -4
- package/dist/{export-KzubBKo7.d.cts → export-D9mXp05Q.d.cts} +1 -1
- package/dist/{export-BDkyDNM8.d.ts → export-DwnZ_hvQ.d.ts} +5 -4
- package/dist/extensions/index.cjs +3 -3
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +2 -2
- package/dist/generation/index.cjs +150 -945
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +3 -423
- package/dist/generation/index.d.ts +3 -423
- package/dist/generation/index.js +59 -854
- package/dist/generation/index.js.map +1 -1
- package/dist/grammar/index.cjs +3 -3
- package/dist/grammar/index.cjs.map +1 -1
- package/dist/grammar/index.js +2 -2
- package/dist/{helpers-Coa4e2j_.d.ts → helpers-C-RgeyvH.d.ts} +25 -8
- package/dist/{helpers-DHl28yLO.d.cts → helpers-v6GMazzg.d.cts} +25 -8
- package/dist/image-editor/index.cjs +986 -986
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +15 -11
- package/dist/image-editor/index.d.ts +15 -11
- package/dist/image-editor/index.js +94 -94
- package/dist/index.cjs +28 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +36 -36
- package/dist/{opentype-BWR7Qv8W.d.cts → opentype-KR_jj6b_.d.cts} +1 -1
- package/dist/{opentype-Dfcx6wFx.d.ts → opentype-a1KAW-qs.d.ts} +1 -1
- package/dist/paint/index.cjs +5 -8
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +4 -2
- package/dist/paint/index.d.ts +4 -2
- package/dist/paint/index.js +15 -18
- package/dist/paint-editor/index.cjs +405 -403
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +15 -748
- package/dist/paint-editor/index.d.ts +15 -748
- package/dist/paint-editor/index.js +79 -77
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/{pathfinder-D8KQ6blQ.d.cts → pathfinder-BHS4vQNm.d.cts} +1 -1
- package/dist/{pathfinder-Celbi3e6.d.ts → pathfinder-BVvTIBMo.d.ts} +1 -1
- package/dist/{pool-8JZGWHvR.d.ts → pool-CZQOrSXZ.d.ts} +1 -1
- package/dist/{pool-BfuxBKTd.d.cts → pool-FjZZmZ80.d.cts} +1 -1
- package/dist/provenance-DmlcgdzT.d.cts +426 -0
- package/dist/provenance-uh5r2TQ8.d.ts +426 -0
- package/dist/rand-8xbUb6ur.d.ts +1118 -0
- package/dist/rand-Cw9VLrVC.d.cts +1118 -0
- package/dist/raster/index.cjs +17 -19
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.js +9 -11
- package/dist/raster/index.js.map +1 -1
- package/dist/{svg-CxxqG3_W.d.ts → svg-CSz3mzkw.d.ts} +2 -2
- package/dist/{svg-pwXWq6s2.d.cts → svg-DTb40ICU.d.cts} +2 -2
- package/dist/{tools-y1M6O3UT.d.ts → tools-B0wXP5cF.d.ts} +1 -1
- package/dist/{tools-3EBjR91b.d.cts → tools-Ca0kI1qo.d.cts} +1 -1
- package/dist/vector/index.cjs +2698 -1674
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +390 -72
- package/dist/vector/index.d.ts +390 -72
- package/dist/vector/index.js +1665 -641
- package/dist/vector/index.js.map +1 -1
- package/dist/video-editor/index.d.cts +7 -4
- package/dist/video-editor/index.d.ts +7 -4
- package/dist/{warp-Ceqdz7kj.d.cts → warp-DTOp0lON.d.cts} +1 -1
- package/dist/{warp-C1gwu2G_.d.ts → warp-pd7hrH4T.d.ts} +1 -1
- package/package.json +1 -1
- package/skills/pixerate-vector/SKILL.md +22 -0
- package/dist/brushstrokeRender-JNHVEUFG.cjs.map +0 -1
- package/dist/chunk-64PYNEHJ.js +0 -340
- package/dist/chunk-64PYNEHJ.js.map +0 -1
- package/dist/chunk-6GFRKLLS.cjs.map +0 -1
- package/dist/chunk-7QN76ESJ.js +0 -323
- package/dist/chunk-7QN76ESJ.js.map +0 -1
- package/dist/chunk-BR7EKQUP.js.map +0 -1
- package/dist/chunk-DCLJNVZS.cjs +0 -323
- package/dist/chunk-DCLJNVZS.cjs.map +0 -1
- package/dist/chunk-EM3FMJWE.js.map +0 -1
- package/dist/chunk-HHTCDFZC.js.map +0 -1
- package/dist/chunk-J5BA3BQ6.cjs.map +0 -1
- package/dist/chunk-YAFS2QFZ.cjs.map +0 -1
- package/dist/chunk-Z2H7KECK.cjs.map +0 -1
- package/dist/chunk-Z6OUCS2K.cjs +0 -340
- package/dist/chunk-Z6OUCS2K.cjs.map +0 -1
- package/dist/rand-BszBST4s.d.ts +0 -2140
- package/dist/rand-DOH_aMvm.d.cts +0 -2140
- /package/dist/{chunk-62XUO5LB.js.map → chunk-3X6IAJUQ.js.map} +0 -0
- /package/dist/{chunk-7RJ6P7MI.js.map → chunk-6S6362D2.js.map} +0 -0
- /package/dist/{chunk-SVYE5HIM.js.map → chunk-EQQMGO42.js.map} +0 -0
- /package/dist/{chunk-OXSV64JX.js.map → chunk-G6IKY6PL.js.map} +0 -0
- /package/dist/{chunk-UYQUPUR6.js.map → chunk-HUO3TI4C.js.map} +0 -0
- /package/dist/{chunk-2HC4DRF4.js.map → chunk-LLFHCSU2.js.map} +0 -0
- /package/dist/{chunk-ILYFOJUN.js.map → chunk-OIBIRXRG.js.map} +0 -0
- /package/dist/{chunk-FWEO3HSL.js.map → chunk-PQQF5KOW.js.map} +0 -0
|
@@ -1,18 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BRUSHSTROKE_MAX_SCALE,
|
|
3
|
+
BRUSHSTROKE_MIN_SCALE
|
|
4
|
+
} from "./chunk-63AZAUSN.js";
|
|
1
5
|
import {
|
|
2
6
|
PIGMENT_BANDS,
|
|
3
7
|
TILE,
|
|
8
|
+
TiledSurface,
|
|
4
9
|
blendPixel,
|
|
5
10
|
createPigment,
|
|
11
|
+
emptySurface,
|
|
12
|
+
finishTile,
|
|
6
13
|
lerpPigmentInto,
|
|
7
14
|
mixPigment8,
|
|
8
15
|
mixPigmentRgb,
|
|
9
16
|
pigment8,
|
|
17
|
+
pngDataUrl,
|
|
18
|
+
readRegion,
|
|
10
19
|
setPigmentColor,
|
|
11
20
|
srgbDecode,
|
|
12
21
|
srgbEncode,
|
|
13
22
|
tileCoord,
|
|
14
|
-
tileKey
|
|
15
|
-
|
|
23
|
+
tileKey,
|
|
24
|
+
tilesInRect
|
|
25
|
+
} from "./chunk-3G6J7UFV.js";
|
|
16
26
|
|
|
17
27
|
// src/paint/rand.ts
|
|
18
28
|
function mix32(h) {
|
|
@@ -1090,6 +1100,302 @@ function coverageTileCoord(key) {
|
|
|
1090
1100
|
return tileCoord(key);
|
|
1091
1101
|
}
|
|
1092
1102
|
|
|
1103
|
+
// src/paint/path-stroke.ts
|
|
1104
|
+
function lean(s) {
|
|
1105
|
+
if (typeof s.altitude === "number" && Number.isFinite(s.altitude)) {
|
|
1106
|
+
return { amount: 1 - Math.min(1, Math.max(0, s.altitude / (Math.PI / 2))), azimuth: s.azimuth ?? 0 };
|
|
1107
|
+
}
|
|
1108
|
+
if (typeof s.tiltX === "number" || typeof s.tiltY === "number") {
|
|
1109
|
+
const tx = (s.tiltX ?? 0) * Math.PI / 180;
|
|
1110
|
+
const ty = (s.tiltY ?? 0) * Math.PI / 180;
|
|
1111
|
+
if (tx === 0 && ty === 0) return { amount: 0, azimuth: 0 };
|
|
1112
|
+
const az = Math.atan2(Math.tan(ty), Math.tan(tx));
|
|
1113
|
+
const alt = Math.atan(1 / Math.hypot(Math.tan(tx), Math.tan(ty)));
|
|
1114
|
+
return { amount: 1 - alt / (Math.PI / 2), azimuth: az };
|
|
1115
|
+
}
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
function pathStrokeDabs(samples, options) {
|
|
1119
|
+
const brush = normalizeBrush(options.brush);
|
|
1120
|
+
const pts = options.closed && samples.length > 2 ? [...samples, samples[0]] : [...samples];
|
|
1121
|
+
if (!pts.length) return [];
|
|
1122
|
+
const tilt = options.tilt ?? {};
|
|
1123
|
+
const minSize = Math.min(1, Math.max(0, options.minSize ?? 0));
|
|
1124
|
+
let index = 0;
|
|
1125
|
+
const t0 = pts.find((s) => typeof s.t === "number" && Number.isFinite(s.t))?.t ?? null;
|
|
1126
|
+
const dab = (x, y, p, l, dir, travel = 0, time = null, speed = null) => {
|
|
1127
|
+
const pr = Math.min(1, Math.max(0, p));
|
|
1128
|
+
const lean0 = l?.amount ?? 0;
|
|
1129
|
+
const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * pr : 1) * (1 + 1.5 * (tilt.size ?? 0) * lean0);
|
|
1130
|
+
let angle = brush.angle * Math.PI / 180;
|
|
1131
|
+
if (options.followPath) angle += dir;
|
|
1132
|
+
if (tilt.followAzimuth && l) angle = -l.azimuth;
|
|
1133
|
+
const roundness = Math.max(0.05, brush.roundness * (1 - 0.85 * (tilt.roundness ?? 0) * lean0));
|
|
1134
|
+
const op = brush.pressureOpacity ? pr : 1;
|
|
1135
|
+
const params = { travel, index: index++, pressure: pr, lean: lean0, speed, direction: dir, time: time === null || t0 === null ? null : time - t0 };
|
|
1136
|
+
return { x, y, radius: Math.max(0.5, size / 2), alpha: brush.flow, opacity: brush.opacity * op, angle, roundness, params };
|
|
1137
|
+
};
|
|
1138
|
+
const out = [];
|
|
1139
|
+
const pressure = (s) => typeof s.pressure === "number" && Number.isFinite(s.pressure) ? s.pressure : 1;
|
|
1140
|
+
if (pts.length === 1) {
|
|
1141
|
+
out.push(dab(pts[0].x, pts[0].y, pressure(pts[0]), lean(pts[0]), 0));
|
|
1142
|
+
return out;
|
|
1143
|
+
}
|
|
1144
|
+
let carry = 0;
|
|
1145
|
+
let along = 0;
|
|
1146
|
+
const timeOf = (s) => typeof s.t === "number" && Number.isFinite(s.t) ? s.t : null;
|
|
1147
|
+
out.push(dab(pts[0].x, pts[0].y, pressure(pts[0]), lean(pts[0]), Math.atan2(-(pts[1].y - pts[0].y), pts[1].x - pts[0].x), 0, timeOf(pts[0])));
|
|
1148
|
+
for (let i = 0; i + 1 < pts.length; i++) {
|
|
1149
|
+
const a = pts[i];
|
|
1150
|
+
const b = pts[i + 1];
|
|
1151
|
+
const dx = b.x - a.x;
|
|
1152
|
+
const dy = b.y - a.y;
|
|
1153
|
+
const len = Math.hypot(dx, dy);
|
|
1154
|
+
if (len === 0) continue;
|
|
1155
|
+
const dir = Math.atan2(-dy, dx);
|
|
1156
|
+
const la = lean(a);
|
|
1157
|
+
const lb = lean(b);
|
|
1158
|
+
const ta = timeOf(a);
|
|
1159
|
+
const tb = timeOf(b);
|
|
1160
|
+
const speed = ta !== null && tb !== null && tb > ta ? len / (tb - ta) : null;
|
|
1161
|
+
let t = carry;
|
|
1162
|
+
for (; ; ) {
|
|
1163
|
+
const f = t / len;
|
|
1164
|
+
if (f > 1) break;
|
|
1165
|
+
const p = pressure(a) + (pressure(b) - pressure(a)) * f;
|
|
1166
|
+
const l = la && lb ? { amount: la.amount + (lb.amount - la.amount) * f, azimuth: la.azimuth + (lb.azimuth - la.azimuth) * f } : la ?? lb;
|
|
1167
|
+
const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * p : 1);
|
|
1168
|
+
const step = Math.max(0.5, size * brush.spacing);
|
|
1169
|
+
if (t > 0 || i > 0) out.push(dab(a.x + dx * f, a.y + dy * f, p, l, dir, along + t, ta !== null && tb !== null ? ta + (tb - ta) * f : null, speed));
|
|
1170
|
+
t += step;
|
|
1171
|
+
}
|
|
1172
|
+
carry = t - len;
|
|
1173
|
+
along += len;
|
|
1174
|
+
}
|
|
1175
|
+
return out;
|
|
1176
|
+
}
|
|
1177
|
+
function pathStrokeCoverage(samples, options) {
|
|
1178
|
+
const brush = normalizeBrush(options.brush);
|
|
1179
|
+
const coverage = new CoverageMap();
|
|
1180
|
+
const dabs = pathStrokeDabs(samples, options);
|
|
1181
|
+
const g = options.gradient?.paint.type === "gradient" ? options.gradient : null;
|
|
1182
|
+
const sampler = g ? new PaintGradientSampler(resolvePaintGradient(g.paint, g.colors ?? { paint: "#000000", secondary: "#ffffff" }), g.seed ?? 0) : null;
|
|
1183
|
+
const tipTable = sampler?.acrossTip ? sampler.tipTable(TIP_GRADIENT_STEPS) : null;
|
|
1184
|
+
const scratch = { field: new Float32Array(0), sample: { rgb: [0, 0, 0], alpha: 1 } };
|
|
1185
|
+
let buf = new Float32Array(0);
|
|
1186
|
+
let bounds = null;
|
|
1187
|
+
for (const d of dabs) {
|
|
1188
|
+
const rect = dabRect(d, brush.hardness);
|
|
1189
|
+
const n = rect.width * rect.height;
|
|
1190
|
+
if (n <= 0) continue;
|
|
1191
|
+
if (buf.length < n) buf = new Float32Array(n);
|
|
1192
|
+
const vals = buf.subarray(0, n);
|
|
1193
|
+
rasterizeDab(d, brush.hardness, rect, vals);
|
|
1194
|
+
if (sampler) {
|
|
1195
|
+
const { color, field } = gradientDabColor(sampler, tipTable, d, d.params, rect, vals, scratch);
|
|
1196
|
+
coverage.accumulate(rect, vals, d.opacity, color, field);
|
|
1197
|
+
} else coverage.accumulate(rect, vals, d.opacity);
|
|
1198
|
+
if (!bounds) bounds = { ...rect };
|
|
1199
|
+
else {
|
|
1200
|
+
const x = Math.min(bounds.x, rect.x);
|
|
1201
|
+
const y = Math.min(bounds.y, rect.y);
|
|
1202
|
+
bounds = { x, y, width: Math.max(bounds.x + bounds.width, rect.x + rect.width) - x, height: Math.max(bounds.y + bounds.height, rect.y + rect.height) - y };
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
return { coverage, bounds, dabs: dabs.length };
|
|
1206
|
+
}
|
|
1207
|
+
function paintPathStroke(surface, samples, options) {
|
|
1208
|
+
if (surface.channels !== 4) throw new Error("paintPathStroke paints RGBA surfaces (4 channels).");
|
|
1209
|
+
const brush = normalizeBrush(options.brush);
|
|
1210
|
+
const hex = (c) => "#" + [0, 1, 2].map((i) => Math.round(Math.min(255, Math.max(0, c[i]))).toString(16).padStart(2, "0")).join("");
|
|
1211
|
+
let color = options.color;
|
|
1212
|
+
if (options.paint?.paint.type === "color") {
|
|
1213
|
+
const rgb = hexToPaintRgb(options.paint.paint.color).map((v) => Math.round(v * 255));
|
|
1214
|
+
color = [rgb[0], rgb[1], rgb[2], color[3]];
|
|
1215
|
+
}
|
|
1216
|
+
const gradient = options.paint?.paint.type === "gradient" ? { ...options.paint, colors: options.paint.colors ?? { paint: hex(color), secondary: "#ffffff" } } : null;
|
|
1217
|
+
const { coverage, bounds, dabs } = pathStrokeCoverage(samples, { ...options, gradient });
|
|
1218
|
+
if (!bounds) return { surface, bounds: null, dabs };
|
|
1219
|
+
const offset = options.offset ?? { x: 0, y: 0 };
|
|
1220
|
+
const apply = {
|
|
1221
|
+
mode: options.mode ?? "paint",
|
|
1222
|
+
color,
|
|
1223
|
+
background: [0, 0, 0, 0],
|
|
1224
|
+
opacity: brush.opacity,
|
|
1225
|
+
blend: options.blend ?? brush.blend,
|
|
1226
|
+
lockTransparency: options.lockTransparency ?? false,
|
|
1227
|
+
selection: options.selection ?? null,
|
|
1228
|
+
offset
|
|
1229
|
+
};
|
|
1230
|
+
const tiles = new Map(surface.tiles);
|
|
1231
|
+
for (const { tx, ty } of tilesInRect({ x: bounds.x - offset.x, y: bounds.y - offset.y, width: bounds.width, height: bounds.height })) {
|
|
1232
|
+
const key = tileKey(tx, ty);
|
|
1233
|
+
const src = surface.tiles.get(key)?.data;
|
|
1234
|
+
const base = src && src.length === TILE * TILE * 4 ? src : new Uint8ClampedArray(TILE * TILE * 4);
|
|
1235
|
+
const t = finishTile(applyStrokeToTile(coverage, base, 4, tx, ty, apply), 4, surface.defaultValue);
|
|
1236
|
+
if (t) tiles.set(key, t);
|
|
1237
|
+
else tiles.delete(key);
|
|
1238
|
+
}
|
|
1239
|
+
return { surface: new TiledSurface(4, tiles, surface.defaultValue), bounds, dabs };
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// src/paint/brushstroke.ts
|
|
1243
|
+
var DEFAULT_MAX_PIXELS = 4096 * 4096;
|
|
1244
|
+
var isProfilePath = (input) => !Array.isArray(input) && typeof input === "object" && input !== null && "points" in input;
|
|
1245
|
+
function profileAt(profile, f) {
|
|
1246
|
+
if (!profile || !profile.length) return void 0;
|
|
1247
|
+
if (profile.length === 1) return profile[0];
|
|
1248
|
+
const u = Math.min(1, Math.max(0, f)) * (profile.length - 1);
|
|
1249
|
+
const i = Math.min(profile.length - 2, Math.floor(u));
|
|
1250
|
+
return profile[i] + (profile[i + 1] - profile[i]) * (u - i);
|
|
1251
|
+
}
|
|
1252
|
+
function brushstrokeSamples(input) {
|
|
1253
|
+
if (!isProfilePath(input)) return input.map((s) => ({ ...s }));
|
|
1254
|
+
const pts = input.points;
|
|
1255
|
+
const lengths = [0];
|
|
1256
|
+
for (let i = 1; i < pts.length; i++) lengths.push(lengths[i - 1] + Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y));
|
|
1257
|
+
const total = lengths.at(-1) ?? 0;
|
|
1258
|
+
return pts.map((p, i) => {
|
|
1259
|
+
const f = total > 0 ? lengths[i] / total : pts.length > 1 ? i / (pts.length - 1) : 0;
|
|
1260
|
+
const s = { x: p.x, y: p.y };
|
|
1261
|
+
const pressure = profileAt(input.pressure, f);
|
|
1262
|
+
const tiltX = profileAt(input.tiltX, f);
|
|
1263
|
+
const tiltY = profileAt(input.tiltY, f);
|
|
1264
|
+
if (pressure !== void 0) s.pressure = pressure;
|
|
1265
|
+
if (tiltX !== void 0) s.tiltX = tiltX;
|
|
1266
|
+
if (tiltY !== void 0) s.tiltY = tiltY;
|
|
1267
|
+
return s;
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
var REFUSED_ENGINES = {
|
|
1271
|
+
wet: "is a wet brush, and wet paint needs an area to flow in (paint it on a wet layer in the paint editor)",
|
|
1272
|
+
mixer: "is a Mixer Brush, which picks up paint from under the tip and has none on a standalone stroke",
|
|
1273
|
+
blender: "is a blender, which only moves paint that is already there",
|
|
1274
|
+
smudge: "is a smudge brush, which only moves paint that is already there",
|
|
1275
|
+
eraser: "is an eraser, which has nothing to remove on a standalone stroke"
|
|
1276
|
+
};
|
|
1277
|
+
function brushstrokeStrokeOptions(brush) {
|
|
1278
|
+
if (!brush || typeof brush !== "object") throw new Error("A brushstroke needs a brush: tip settings ({ size, hardness, \u2026 }) or a paint brush preset.");
|
|
1279
|
+
if (!("tip" in brush) || !brush.tip) return { brush: normalizeBrush(brush) };
|
|
1280
|
+
const engine = brush.engine ?? "dab";
|
|
1281
|
+
if (engine !== "dab") {
|
|
1282
|
+
const label = brush.name ?? brush.id ?? "This brush";
|
|
1283
|
+
const why = REFUSED_ENGINES[engine] ?? `uses the '${engine}' engine, which renderBrushstroke does not draw`;
|
|
1284
|
+
throw new Error(`Brushstrokes render dry media only: '${label}' ${why}. Pick a dry brush (a dab-engine preset such as a pencil, pastel, charcoal, marker, ink or airbrush) or plain tip settings.`);
|
|
1285
|
+
}
|
|
1286
|
+
const d = brush.dynamics ?? {};
|
|
1287
|
+
const tilt = { size: d.tiltSize ?? 0, roundness: d.tiltRoundness ?? 0 };
|
|
1288
|
+
return {
|
|
1289
|
+
brush: normalizeBrush(brush.tip),
|
|
1290
|
+
followPath: brush.rotation === "stroke",
|
|
1291
|
+
minSize: d.minSize ?? 0,
|
|
1292
|
+
...tilt.size || tilt.roundness ? { tilt } : {}
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
function isBrushstrokeBrush(brush) {
|
|
1296
|
+
return !("tip" in brush) || !brush.tip || (brush.engine ?? "dab") === "dab";
|
|
1297
|
+
}
|
|
1298
|
+
var finite2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
1299
|
+
function renderBrushstroke(input, brush, options = {}) {
|
|
1300
|
+
const strokeOptions = brushstrokeStrokeOptions(brush);
|
|
1301
|
+
const samples = brushstrokeSamples(input);
|
|
1302
|
+
for (const s of samples) if (!finite2(s.x) || !finite2(s.y)) throw new Error("Brushstroke samples need finite x and y.");
|
|
1303
|
+
const solid = options.paint && !("type" in options.paint) ? options.paint.color : null;
|
|
1304
|
+
const source = options.paint && "type" in options.paint ? options.paint : null;
|
|
1305
|
+
const color = solid ?? options.color ?? [0, 0, 0, 255];
|
|
1306
|
+
let scale = finite2(options.scale) && options.scale > 0 ? Math.min(BRUSHSTROKE_MAX_SCALE, Math.max(BRUSHSTROKE_MIN_SCALE, options.scale)) : 1;
|
|
1307
|
+
if (!samples.length) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs: 0 };
|
|
1308
|
+
const tip = strokeOptions.brush;
|
|
1309
|
+
const reach = tip.size * (1 + 1.5 * (strokeOptions.tilt?.size ?? 0)) + 4;
|
|
1310
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
1311
|
+
for (const s of samples) {
|
|
1312
|
+
x0 = Math.min(x0, s.x);
|
|
1313
|
+
y0 = Math.min(y0, s.y);
|
|
1314
|
+
x1 = Math.max(x1, s.x);
|
|
1315
|
+
y1 = Math.max(y1, s.y);
|
|
1316
|
+
}
|
|
1317
|
+
const maxPixels = finite2(options.maxPixels) && options.maxPixels > 0 ? options.maxPixels : DEFAULT_MAX_PIXELS;
|
|
1318
|
+
const area = (x1 - x0 + reach) * (y1 - y0 + reach);
|
|
1319
|
+
if (area * scale * scale > maxPixels) scale = Math.max(BRUSHSTROKE_MIN_SCALE, Math.sqrt(maxPixels / area));
|
|
1320
|
+
const scaled = samples.map((s) => ({ ...s, x: s.x * scale, y: s.y * scale }));
|
|
1321
|
+
const { surface, bounds, dabs } = paintPathStroke(emptySurface(4), scaled, {
|
|
1322
|
+
...strokeOptions,
|
|
1323
|
+
brush: { ...tip, size: tip.size * scale },
|
|
1324
|
+
closed: options.closed ?? strokeOptions.closed,
|
|
1325
|
+
color,
|
|
1326
|
+
// Gradient lengths are in path units: scale them with the image.
|
|
1327
|
+
...source ? { paint: { paint: source.type === "gradient" ? scalePaintGradient(source, scale) : source, seed: options.seed ?? 0 } } : {}
|
|
1328
|
+
});
|
|
1329
|
+
if (!bounds) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs };
|
|
1330
|
+
const rect = bounds;
|
|
1331
|
+
const data = readRegion(surface, rect);
|
|
1332
|
+
return {
|
|
1333
|
+
image: { width: rect.width, height: rect.height, data },
|
|
1334
|
+
bounds: { x: rect.x / scale, y: rect.y / scale, width: rect.width / scale, height: rect.height / scale },
|
|
1335
|
+
scale,
|
|
1336
|
+
dabs
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
function brushstrokeDataUrl(stroke) {
|
|
1340
|
+
const { width, height, data } = stroke.image;
|
|
1341
|
+
return width && height ? pngDataUrl(width, height, data) : "";
|
|
1342
|
+
}
|
|
1343
|
+
function brushstrokeToCanvas(stroke, createCanvas) {
|
|
1344
|
+
const { width, height, data } = stroke.image;
|
|
1345
|
+
if (!width || !height) return null;
|
|
1346
|
+
const canvas = createCanvas(width, height);
|
|
1347
|
+
const ctx = canvas.getContext("2d");
|
|
1348
|
+
if (!ctx) return null;
|
|
1349
|
+
const img = ctx.createImageData(width, height);
|
|
1350
|
+
img.data.set(data);
|
|
1351
|
+
ctx.putImageData(img, 0, 0);
|
|
1352
|
+
return canvas;
|
|
1353
|
+
}
|
|
1354
|
+
function hashString(s, h = 2166136261) {
|
|
1355
|
+
for (let i = 0; i < s.length; i++) {
|
|
1356
|
+
h ^= s.charCodeAt(i);
|
|
1357
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
1358
|
+
}
|
|
1359
|
+
return h;
|
|
1360
|
+
}
|
|
1361
|
+
function brushstrokeKey(input, brush, options = {}) {
|
|
1362
|
+
const path = JSON.stringify(input);
|
|
1363
|
+
const { brush: tip, ...rest } = brushstrokeStrokeOptions(brush);
|
|
1364
|
+
const rest2 = JSON.stringify([tip, rest, options.paint ?? options.color ?? null, options.seed ?? 0, options.closed ?? false, options.maxPixels ?? null]);
|
|
1365
|
+
const scale = finite2(options.scale) ? options.scale : 1;
|
|
1366
|
+
return `${hashString(path).toString(16)}:${path.length}:${hashString(rest2).toString(16)}:${scale}`;
|
|
1367
|
+
}
|
|
1368
|
+
var BrushstrokeCache = class {
|
|
1369
|
+
constructor(capacity = 64) {
|
|
1370
|
+
this.capacity = capacity;
|
|
1371
|
+
}
|
|
1372
|
+
capacity;
|
|
1373
|
+
entries = /* @__PURE__ */ new Map();
|
|
1374
|
+
/** Renders that missed the cache (for tests and stats). */
|
|
1375
|
+
misses = 0;
|
|
1376
|
+
get size() {
|
|
1377
|
+
return this.entries.size;
|
|
1378
|
+
}
|
|
1379
|
+
/** The cached stroke for these arguments, rendering (and caching) it on a miss. */
|
|
1380
|
+
render(input, brush, options = {}) {
|
|
1381
|
+
const key = brushstrokeKey(input, brush, options);
|
|
1382
|
+
const hit = this.entries.get(key);
|
|
1383
|
+
if (hit) {
|
|
1384
|
+
this.entries.delete(key);
|
|
1385
|
+
this.entries.set(key, hit);
|
|
1386
|
+
return hit;
|
|
1387
|
+
}
|
|
1388
|
+
this.misses++;
|
|
1389
|
+
const out = renderBrushstroke(input, brush, options);
|
|
1390
|
+
this.entries.set(key, out);
|
|
1391
|
+
while (this.entries.size > Math.max(1, this.capacity)) this.entries.delete(this.entries.keys().next().value);
|
|
1392
|
+
return out;
|
|
1393
|
+
}
|
|
1394
|
+
clear() {
|
|
1395
|
+
this.entries.clear();
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1093
1399
|
// src/paint/exchange.ts
|
|
1094
1400
|
var MIXING_MODELS = ["rgb", "pigment"];
|
|
1095
1401
|
var EMPTY_MIXER_LOAD = { reservoir: null, amount: 0, pickup: null };
|
|
@@ -1756,6 +2062,17 @@ export {
|
|
|
1756
2062
|
applyStrokeToTile,
|
|
1757
2063
|
applyStrokeToDeepTile,
|
|
1758
2064
|
coverageTileCoord,
|
|
2065
|
+
pathStrokeDabs,
|
|
2066
|
+
pathStrokeCoverage,
|
|
2067
|
+
paintPathStroke,
|
|
2068
|
+
brushstrokeSamples,
|
|
2069
|
+
brushstrokeStrokeOptions,
|
|
2070
|
+
isBrushstrokeBrush,
|
|
2071
|
+
renderBrushstroke,
|
|
2072
|
+
brushstrokeDataUrl,
|
|
2073
|
+
brushstrokeToCanvas,
|
|
2074
|
+
brushstrokeKey,
|
|
2075
|
+
BrushstrokeCache,
|
|
1759
2076
|
MIXING_MODELS,
|
|
1760
2077
|
EMPTY_MIXER_LOAD,
|
|
1761
2078
|
MIXER_PICKUP_RATE,
|
|
@@ -1779,4 +2096,4 @@ export {
|
|
|
1779
2096
|
cleanMixer,
|
|
1780
2097
|
MixerStroke
|
|
1781
2098
|
};
|
|
1782
|
-
//# sourceMappingURL=chunk-
|
|
2099
|
+
//# sourceMappingURL=chunk-5FSRTP6E.js.map
|