@pixerate/editor 0.136.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.
Files changed (132) hide show
  1. package/dist/animation-editor/index.cjs +60 -55
  2. package/dist/animation-editor/index.cjs.map +1 -1
  3. package/dist/animation-editor/index.d.cts +4 -4
  4. package/dist/animation-editor/index.d.ts +4 -4
  5. package/dist/animation-editor/index.js +7 -2
  6. package/dist/animation-editor/index.js.map +1 -1
  7. package/dist/{bezier-CdccFsF8.d.cts → bezier-CZ2AhjID.d.cts} +1 -1
  8. package/dist/{bezier-DG9qNZQV.d.ts → bezier-Dywv3Bw8.d.ts} +1 -1
  9. package/dist/blending-D7jCmjO6.d.cts +1026 -0
  10. package/dist/blending-NgF3qo3d.d.ts +1026 -0
  11. package/dist/brushes-CnvZSYYU.d.cts +744 -0
  12. package/dist/brushes-DJmUbIgz.d.ts +744 -0
  13. package/dist/{brushstrokeRender-SII6MG4R.js → brushstrokeRender-2FAXA3XC.js} +13 -16
  14. package/dist/{brushstrokeRender-SII6MG4R.js.map → brushstrokeRender-2FAXA3XC.js.map} +1 -1
  15. package/dist/{brushstrokeRender-JNHVEUFG.cjs → brushstrokeRender-IMFY3FF6.cjs} +16 -19
  16. package/dist/brushstrokeRender-IMFY3FF6.cjs.map +1 -0
  17. package/dist/canvas/index.cjs +10 -10
  18. package/dist/canvas/index.cjs.map +1 -1
  19. package/dist/canvas/index.d.cts +7 -7
  20. package/dist/canvas/index.d.ts +7 -7
  21. package/dist/canvas/index.js +4 -4
  22. package/dist/{chunk-W4LNKTHT.cjs → chunk-2XS6LTUP.cjs} +63 -63
  23. package/dist/{chunk-W4LNKTHT.cjs.map → chunk-2XS6LTUP.cjs.map} +1 -1
  24. package/dist/{chunk-MAHQC3SK.js → chunk-3D77LOBM.js} +222 -135
  25. package/dist/chunk-3D77LOBM.js.map +1 -0
  26. package/dist/{chunk-BR7EKQUP.js → chunk-3G6J7UFV.js} +322 -2
  27. package/dist/chunk-3G6J7UFV.js.map +1 -0
  28. package/dist/{chunk-62XUO5LB.js → chunk-3X6IAJUQ.js} +2 -2
  29. package/dist/{chunk-EM3FMJWE.js → chunk-5FSRTP6E.js} +320 -3
  30. package/dist/chunk-5FSRTP6E.js.map +1 -0
  31. package/dist/{chunk-2R3UW57U.cjs → chunk-5OOIUXNJ.cjs} +333 -246
  32. package/dist/chunk-5OOIUXNJ.cjs.map +1 -0
  33. package/dist/{chunk-KZXLZCQ7.js → chunk-6S6362D2.js} +2 -2
  34. package/dist/{chunk-24ATT2PE.cjs → chunk-AEAAN4AD.cjs} +120 -120
  35. package/dist/{chunk-24ATT2PE.cjs.map → chunk-AEAAN4AD.cjs.map} +1 -1
  36. package/dist/{chunk-SVYE5HIM.js → chunk-EQQMGO42.js} +2 -2
  37. package/dist/{chunk-FHPVDTI2.cjs → chunk-F2Y4E4GU.cjs} +22 -22
  38. package/dist/{chunk-FHPVDTI2.cjs.map → chunk-F2Y4E4GU.cjs.map} +1 -1
  39. package/dist/{chunk-UYQUPUR6.js → chunk-HUO3TI4C.js} +3 -3
  40. package/dist/{chunk-DDSO4HS7.cjs → chunk-KEENUAFN.cjs} +2 -2
  41. package/dist/{chunk-DDSO4HS7.cjs.map → chunk-KEENUAFN.cjs.map} +1 -1
  42. package/dist/{chunk-2HC4DRF4.js → chunk-LLFHCSU2.js} +2 -2
  43. package/dist/{chunk-J5BA3BQ6.cjs → chunk-O7LUD7MH.cjs} +323 -3
  44. package/dist/chunk-O7LUD7MH.cjs.map +1 -0
  45. package/dist/{chunk-ILYFOJUN.js → chunk-OIBIRXRG.js} +3 -3
  46. package/dist/{chunk-B2CUCM7Y.cjs → chunk-PRSRYHQD.cjs} +36 -36
  47. package/dist/{chunk-B2CUCM7Y.cjs.map → chunk-PRSRYHQD.cjs.map} +1 -1
  48. package/dist/{chunk-6GFRKLLS.cjs → chunk-QBWEH3IM.cjs} +429 -112
  49. package/dist/chunk-QBWEH3IM.cjs.map +1 -0
  50. package/dist/{chunk-6QQR4LKE.js → chunk-SYT2TLQF.js} +35 -37
  51. package/dist/{chunk-6QQR4LKE.js.map → chunk-SYT2TLQF.js.map} +1 -1
  52. package/dist/{chunk-YT3JL7YM.cjs → chunk-TLCTTQFF.cjs} +53 -53
  53. package/dist/{chunk-YT3JL7YM.cjs.map → chunk-TLCTTQFF.cjs.map} +1 -1
  54. package/dist/{chunk-JLGNZMUV.cjs → chunk-XN2NBLV5.cjs} +313 -315
  55. package/dist/chunk-XN2NBLV5.cjs.map +1 -0
  56. package/dist/effects/index.d.cts +4 -4
  57. package/dist/effects/index.d.ts +4 -4
  58. package/dist/{export-Ds7zgGD8.d.ts → export-41rL6Cne.d.ts} +1 -1
  59. package/dist/{export-BOheWw2R.d.cts → export-CyJdfP_Z.d.cts} +5 -4
  60. package/dist/{export-CEYeRQXH.d.cts → export-D9mXp05Q.d.cts} +1 -1
  61. package/dist/{export-ByKKIA2S.d.ts → export-DwnZ_hvQ.d.ts} +5 -4
  62. package/dist/{helpers--ns3W2D7.d.ts → helpers-C-RgeyvH.d.ts} +18 -7
  63. package/dist/{helpers-BVkU4oUi.d.cts → helpers-v6GMazzg.d.cts} +18 -7
  64. package/dist/image-editor/index.cjs +984 -987
  65. package/dist/image-editor/index.cjs.map +1 -1
  66. package/dist/image-editor/index.d.cts +12 -11
  67. package/dist/image-editor/index.d.ts +12 -11
  68. package/dist/image-editor/index.js +92 -95
  69. package/dist/image-editor/index.js.map +1 -1
  70. package/dist/{opentype-yi-Kz4ND.d.cts → opentype-KR_jj6b_.d.cts} +1 -1
  71. package/dist/{opentype-DfoVYgmG.d.ts → opentype-a1KAW-qs.d.ts} +1 -1
  72. package/dist/paint/index.cjs +5 -8
  73. package/dist/paint/index.cjs.map +1 -1
  74. package/dist/paint/index.d.cts +4 -2
  75. package/dist/paint/index.d.ts +4 -2
  76. package/dist/paint/index.js +15 -18
  77. package/dist/paint-editor/index.cjs +403 -404
  78. package/dist/paint-editor/index.cjs.map +1 -1
  79. package/dist/paint-editor/index.d.cts +12 -748
  80. package/dist/paint-editor/index.d.ts +12 -748
  81. package/dist/paint-editor/index.js +77 -78
  82. package/dist/paint-editor/index.js.map +1 -1
  83. package/dist/{pathfinder-7JkMwYvC.d.cts → pathfinder-BHS4vQNm.d.cts} +1 -1
  84. package/dist/{pathfinder-GM3UWdBZ.d.ts → pathfinder-BVvTIBMo.d.ts} +1 -1
  85. package/dist/{pool-CkTxlLMC.d.ts → pool-CZQOrSXZ.d.ts} +1 -1
  86. package/dist/{pool-04qp8iW4.d.cts → pool-FjZZmZ80.d.cts} +1 -1
  87. package/dist/rand-8xbUb6ur.d.ts +1118 -0
  88. package/dist/rand-Cw9VLrVC.d.cts +1118 -0
  89. package/dist/raster/index.cjs +17 -19
  90. package/dist/raster/index.cjs.map +1 -1
  91. package/dist/raster/index.js +9 -11
  92. package/dist/raster/index.js.map +1 -1
  93. package/dist/{svg-rV6_HPmn.d.ts → svg-CSz3mzkw.d.ts} +2 -2
  94. package/dist/{svg-BDDKvWWY.d.cts → svg-DTb40ICU.d.cts} +2 -2
  95. package/dist/{tools-DPYerqeR.d.ts → tools-B0wXP5cF.d.ts} +1 -1
  96. package/dist/{tools-BAjSbTdY.d.cts → tools-Ca0kI1qo.d.cts} +1 -1
  97. package/dist/vector/index.cjs +1459 -1259
  98. package/dist/vector/index.cjs.map +1 -1
  99. package/dist/vector/index.d.cts +87 -11
  100. package/dist/vector/index.d.ts +87 -11
  101. package/dist/vector/index.js +213 -13
  102. package/dist/vector/index.js.map +1 -1
  103. package/dist/video-editor/index.d.cts +4 -4
  104. package/dist/video-editor/index.d.ts +4 -4
  105. package/dist/{warp-DQh9R7Ie.d.cts → warp-DTOp0lON.d.cts} +1 -1
  106. package/dist/{warp-XVL6SpSh.d.ts → warp-pd7hrH4T.d.ts} +1 -1
  107. package/package.json +1 -1
  108. package/skills/pixerate-vector/SKILL.md +8 -0
  109. package/dist/brushstrokeRender-JNHVEUFG.cjs.map +0 -1
  110. package/dist/chunk-2R3UW57U.cjs.map +0 -1
  111. package/dist/chunk-64PYNEHJ.js +0 -340
  112. package/dist/chunk-64PYNEHJ.js.map +0 -1
  113. package/dist/chunk-6GFRKLLS.cjs.map +0 -1
  114. package/dist/chunk-7QN76ESJ.js +0 -323
  115. package/dist/chunk-7QN76ESJ.js.map +0 -1
  116. package/dist/chunk-BR7EKQUP.js.map +0 -1
  117. package/dist/chunk-DCLJNVZS.cjs +0 -323
  118. package/dist/chunk-DCLJNVZS.cjs.map +0 -1
  119. package/dist/chunk-EM3FMJWE.js.map +0 -1
  120. package/dist/chunk-J5BA3BQ6.cjs.map +0 -1
  121. package/dist/chunk-JLGNZMUV.cjs.map +0 -1
  122. package/dist/chunk-MAHQC3SK.js.map +0 -1
  123. package/dist/chunk-Z6OUCS2K.cjs +0 -340
  124. package/dist/chunk-Z6OUCS2K.cjs.map +0 -1
  125. package/dist/rand-BszBST4s.d.ts +0 -2140
  126. package/dist/rand-DOH_aMvm.d.cts +0 -2140
  127. /package/dist/{chunk-62XUO5LB.js.map → chunk-3X6IAJUQ.js.map} +0 -0
  128. /package/dist/{chunk-KZXLZCQ7.js.map → chunk-6S6362D2.js.map} +0 -0
  129. /package/dist/{chunk-SVYE5HIM.js.map → chunk-EQQMGO42.js.map} +0 -0
  130. /package/dist/{chunk-UYQUPUR6.js.map → chunk-HUO3TI4C.js.map} +0 -0
  131. /package/dist/{chunk-2HC4DRF4.js.map → chunk-LLFHCSU2.js.map} +0 -0
  132. /package/dist/{chunk-ILYFOJUN.js.map → chunk-OIBIRXRG.js.map} +0 -0
@@ -1,340 +0,0 @@
1
- import {
2
- CoverageMap,
3
- PaintGradientSampler,
4
- TIP_GRADIENT_STEPS,
5
- applyStrokeToTile,
6
- dabRect,
7
- gradientDabColor,
8
- hexToPaintRgb,
9
- normalizeBrush,
10
- rasterizeDab,
11
- resolvePaintGradient,
12
- scalePaintGradient
13
- } from "./chunk-EM3FMJWE.js";
14
- import {
15
- BRUSHSTROKE_MAX_SCALE,
16
- BRUSHSTROKE_MIN_SCALE
17
- } from "./chunk-63AZAUSN.js";
18
- import {
19
- TILE,
20
- TiledSurface,
21
- emptySurface,
22
- finishTile,
23
- readRegion,
24
- tileKey,
25
- tilesInRect
26
- } from "./chunk-BR7EKQUP.js";
27
- import {
28
- pngDataUrl
29
- } from "./chunk-7QN76ESJ.js";
30
-
31
- // src/paint/path-stroke.ts
32
- function lean(s) {
33
- if (typeof s.altitude === "number" && Number.isFinite(s.altitude)) {
34
- return { amount: 1 - Math.min(1, Math.max(0, s.altitude / (Math.PI / 2))), azimuth: s.azimuth ?? 0 };
35
- }
36
- if (typeof s.tiltX === "number" || typeof s.tiltY === "number") {
37
- const tx = (s.tiltX ?? 0) * Math.PI / 180;
38
- const ty = (s.tiltY ?? 0) * Math.PI / 180;
39
- if (tx === 0 && ty === 0) return { amount: 0, azimuth: 0 };
40
- const az = Math.atan2(Math.tan(ty), Math.tan(tx));
41
- const alt = Math.atan(1 / Math.hypot(Math.tan(tx), Math.tan(ty)));
42
- return { amount: 1 - alt / (Math.PI / 2), azimuth: az };
43
- }
44
- return null;
45
- }
46
- function pathStrokeDabs(samples, options) {
47
- const brush = normalizeBrush(options.brush);
48
- const pts = options.closed && samples.length > 2 ? [...samples, samples[0]] : [...samples];
49
- if (!pts.length) return [];
50
- const tilt = options.tilt ?? {};
51
- const minSize = Math.min(1, Math.max(0, options.minSize ?? 0));
52
- let index = 0;
53
- const t0 = pts.find((s) => typeof s.t === "number" && Number.isFinite(s.t))?.t ?? null;
54
- const dab = (x, y, p, l, dir, travel = 0, time = null, speed = null) => {
55
- const pr = Math.min(1, Math.max(0, p));
56
- const lean0 = l?.amount ?? 0;
57
- const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * pr : 1) * (1 + 1.5 * (tilt.size ?? 0) * lean0);
58
- let angle = brush.angle * Math.PI / 180;
59
- if (options.followPath) angle += dir;
60
- if (tilt.followAzimuth && l) angle = -l.azimuth;
61
- const roundness = Math.max(0.05, brush.roundness * (1 - 0.85 * (tilt.roundness ?? 0) * lean0));
62
- const op = brush.pressureOpacity ? pr : 1;
63
- const params = { travel, index: index++, pressure: pr, lean: lean0, speed, direction: dir, time: time === null || t0 === null ? null : time - t0 };
64
- return { x, y, radius: Math.max(0.5, size / 2), alpha: brush.flow, opacity: brush.opacity * op, angle, roundness, params };
65
- };
66
- const out = [];
67
- const pressure = (s) => typeof s.pressure === "number" && Number.isFinite(s.pressure) ? s.pressure : 1;
68
- if (pts.length === 1) {
69
- out.push(dab(pts[0].x, pts[0].y, pressure(pts[0]), lean(pts[0]), 0));
70
- return out;
71
- }
72
- let carry = 0;
73
- let along = 0;
74
- const timeOf = (s) => typeof s.t === "number" && Number.isFinite(s.t) ? s.t : null;
75
- 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])));
76
- for (let i = 0; i + 1 < pts.length; i++) {
77
- const a = pts[i];
78
- const b = pts[i + 1];
79
- const dx = b.x - a.x;
80
- const dy = b.y - a.y;
81
- const len = Math.hypot(dx, dy);
82
- if (len === 0) continue;
83
- const dir = Math.atan2(-dy, dx);
84
- const la = lean(a);
85
- const lb = lean(b);
86
- const ta = timeOf(a);
87
- const tb = timeOf(b);
88
- const speed = ta !== null && tb !== null && tb > ta ? len / (tb - ta) : null;
89
- let t = carry;
90
- for (; ; ) {
91
- const f = t / len;
92
- if (f > 1) break;
93
- const p = pressure(a) + (pressure(b) - pressure(a)) * f;
94
- const l = la && lb ? { amount: la.amount + (lb.amount - la.amount) * f, azimuth: la.azimuth + (lb.azimuth - la.azimuth) * f } : la ?? lb;
95
- const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * p : 1);
96
- const step = Math.max(0.5, size * brush.spacing);
97
- 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));
98
- t += step;
99
- }
100
- carry = t - len;
101
- along += len;
102
- }
103
- return out;
104
- }
105
- function pathStrokeCoverage(samples, options) {
106
- const brush = normalizeBrush(options.brush);
107
- const coverage = new CoverageMap();
108
- const dabs = pathStrokeDabs(samples, options);
109
- const g = options.gradient?.paint.type === "gradient" ? options.gradient : null;
110
- const sampler = g ? new PaintGradientSampler(resolvePaintGradient(g.paint, g.colors ?? { paint: "#000000", secondary: "#ffffff" }), g.seed ?? 0) : null;
111
- const tipTable = sampler?.acrossTip ? sampler.tipTable(TIP_GRADIENT_STEPS) : null;
112
- const scratch = { field: new Float32Array(0), sample: { rgb: [0, 0, 0], alpha: 1 } };
113
- let buf = new Float32Array(0);
114
- let bounds = null;
115
- for (const d of dabs) {
116
- const rect = dabRect(d, brush.hardness);
117
- const n = rect.width * rect.height;
118
- if (n <= 0) continue;
119
- if (buf.length < n) buf = new Float32Array(n);
120
- const vals = buf.subarray(0, n);
121
- rasterizeDab(d, brush.hardness, rect, vals);
122
- if (sampler) {
123
- const { color, field } = gradientDabColor(sampler, tipTable, d, d.params, rect, vals, scratch);
124
- coverage.accumulate(rect, vals, d.opacity, color, field);
125
- } else coverage.accumulate(rect, vals, d.opacity);
126
- if (!bounds) bounds = { ...rect };
127
- else {
128
- const x = Math.min(bounds.x, rect.x);
129
- const y = Math.min(bounds.y, rect.y);
130
- 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 };
131
- }
132
- }
133
- return { coverage, bounds, dabs: dabs.length };
134
- }
135
- function paintPathStroke(surface, samples, options) {
136
- if (surface.channels !== 4) throw new Error("paintPathStroke paints RGBA surfaces (4 channels).");
137
- const brush = normalizeBrush(options.brush);
138
- const hex = (c) => "#" + [0, 1, 2].map((i) => Math.round(Math.min(255, Math.max(0, c[i]))).toString(16).padStart(2, "0")).join("");
139
- let color = options.color;
140
- if (options.paint?.paint.type === "color") {
141
- const rgb = hexToPaintRgb(options.paint.paint.color).map((v) => Math.round(v * 255));
142
- color = [rgb[0], rgb[1], rgb[2], color[3]];
143
- }
144
- const gradient = options.paint?.paint.type === "gradient" ? { ...options.paint, colors: options.paint.colors ?? { paint: hex(color), secondary: "#ffffff" } } : null;
145
- const { coverage, bounds, dabs } = pathStrokeCoverage(samples, { ...options, gradient });
146
- if (!bounds) return { surface, bounds: null, dabs };
147
- const offset = options.offset ?? { x: 0, y: 0 };
148
- const apply = {
149
- mode: options.mode ?? "paint",
150
- color,
151
- background: [0, 0, 0, 0],
152
- opacity: brush.opacity,
153
- blend: options.blend ?? brush.blend,
154
- lockTransparency: options.lockTransparency ?? false,
155
- selection: options.selection ?? null,
156
- offset
157
- };
158
- const tiles = new Map(surface.tiles);
159
- for (const { tx, ty } of tilesInRect({ x: bounds.x - offset.x, y: bounds.y - offset.y, width: bounds.width, height: bounds.height })) {
160
- const key = tileKey(tx, ty);
161
- const src = surface.tiles.get(key)?.data;
162
- const base = src && src.length === TILE * TILE * 4 ? src : new Uint8ClampedArray(TILE * TILE * 4);
163
- const t = finishTile(applyStrokeToTile(coverage, base, 4, tx, ty, apply), 4, surface.defaultValue);
164
- if (t) tiles.set(key, t);
165
- else tiles.delete(key);
166
- }
167
- return { surface: new TiledSurface(4, tiles, surface.defaultValue), bounds, dabs };
168
- }
169
-
170
- // src/paint/brushstroke.ts
171
- var DEFAULT_MAX_PIXELS = 4096 * 4096;
172
- var isProfilePath = (input) => !Array.isArray(input) && typeof input === "object" && input !== null && "points" in input;
173
- function profileAt(profile, f) {
174
- if (!profile || !profile.length) return void 0;
175
- if (profile.length === 1) return profile[0];
176
- const u = Math.min(1, Math.max(0, f)) * (profile.length - 1);
177
- const i = Math.min(profile.length - 2, Math.floor(u));
178
- return profile[i] + (profile[i + 1] - profile[i]) * (u - i);
179
- }
180
- function brushstrokeSamples(input) {
181
- if (!isProfilePath(input)) return input.map((s) => ({ ...s }));
182
- const pts = input.points;
183
- const lengths = [0];
184
- 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));
185
- const total = lengths.at(-1) ?? 0;
186
- return pts.map((p, i) => {
187
- const f = total > 0 ? lengths[i] / total : pts.length > 1 ? i / (pts.length - 1) : 0;
188
- const s = { x: p.x, y: p.y };
189
- const pressure = profileAt(input.pressure, f);
190
- const tiltX = profileAt(input.tiltX, f);
191
- const tiltY = profileAt(input.tiltY, f);
192
- if (pressure !== void 0) s.pressure = pressure;
193
- if (tiltX !== void 0) s.tiltX = tiltX;
194
- if (tiltY !== void 0) s.tiltY = tiltY;
195
- return s;
196
- });
197
- }
198
- var REFUSED_ENGINES = {
199
- wet: "is a wet brush, and wet paint needs an area to flow in (paint it on a wet layer in the paint editor)",
200
- mixer: "is a Mixer Brush, which picks up paint from under the tip and has none on a standalone stroke",
201
- blender: "is a blender, which only moves paint that is already there",
202
- smudge: "is a smudge brush, which only moves paint that is already there",
203
- eraser: "is an eraser, which has nothing to remove on a standalone stroke"
204
- };
205
- function brushstrokeStrokeOptions(brush) {
206
- if (!brush || typeof brush !== "object") throw new Error("A brushstroke needs a brush: tip settings ({ size, hardness, \u2026 }) or a paint brush preset.");
207
- if (!("tip" in brush) || !brush.tip) return { brush: normalizeBrush(brush) };
208
- const engine = brush.engine ?? "dab";
209
- if (engine !== "dab") {
210
- const label = brush.name ?? brush.id ?? "This brush";
211
- const why = REFUSED_ENGINES[engine] ?? `uses the '${engine}' engine, which renderBrushstroke does not draw`;
212
- 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.`);
213
- }
214
- const d = brush.dynamics ?? {};
215
- const tilt = { size: d.tiltSize ?? 0, roundness: d.tiltRoundness ?? 0 };
216
- return {
217
- brush: normalizeBrush(brush.tip),
218
- followPath: brush.rotation === "stroke",
219
- minSize: d.minSize ?? 0,
220
- ...tilt.size || tilt.roundness ? { tilt } : {}
221
- };
222
- }
223
- function isBrushstrokeBrush(brush) {
224
- return !("tip" in brush) || !brush.tip || (brush.engine ?? "dab") === "dab";
225
- }
226
- var finite = (v) => typeof v === "number" && Number.isFinite(v);
227
- function renderBrushstroke(input, brush, options = {}) {
228
- const strokeOptions = brushstrokeStrokeOptions(brush);
229
- const samples = brushstrokeSamples(input);
230
- for (const s of samples) if (!finite(s.x) || !finite(s.y)) throw new Error("Brushstroke samples need finite x and y.");
231
- const solid = options.paint && !("type" in options.paint) ? options.paint.color : null;
232
- const source = options.paint && "type" in options.paint ? options.paint : null;
233
- const color = solid ?? options.color ?? [0, 0, 0, 255];
234
- let scale = finite(options.scale) && options.scale > 0 ? Math.min(BRUSHSTROKE_MAX_SCALE, Math.max(BRUSHSTROKE_MIN_SCALE, options.scale)) : 1;
235
- if (!samples.length) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs: 0 };
236
- const tip = strokeOptions.brush;
237
- const reach = tip.size * (1 + 1.5 * (strokeOptions.tilt?.size ?? 0)) + 4;
238
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
239
- for (const s of samples) {
240
- x0 = Math.min(x0, s.x);
241
- y0 = Math.min(y0, s.y);
242
- x1 = Math.max(x1, s.x);
243
- y1 = Math.max(y1, s.y);
244
- }
245
- const maxPixels = finite(options.maxPixels) && options.maxPixels > 0 ? options.maxPixels : DEFAULT_MAX_PIXELS;
246
- const area = (x1 - x0 + reach) * (y1 - y0 + reach);
247
- if (area * scale * scale > maxPixels) scale = Math.max(BRUSHSTROKE_MIN_SCALE, Math.sqrt(maxPixels / area));
248
- const scaled = samples.map((s) => ({ ...s, x: s.x * scale, y: s.y * scale }));
249
- const { surface, bounds, dabs } = paintPathStroke(emptySurface(4), scaled, {
250
- ...strokeOptions,
251
- brush: { ...tip, size: tip.size * scale },
252
- closed: options.closed ?? strokeOptions.closed,
253
- color,
254
- // Gradient lengths are in path units: scale them with the image.
255
- ...source ? { paint: { paint: source.type === "gradient" ? scalePaintGradient(source, scale) : source, seed: options.seed ?? 0 } } : {}
256
- });
257
- if (!bounds) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs };
258
- const rect = bounds;
259
- const data = readRegion(surface, rect);
260
- return {
261
- image: { width: rect.width, height: rect.height, data },
262
- bounds: { x: rect.x / scale, y: rect.y / scale, width: rect.width / scale, height: rect.height / scale },
263
- scale,
264
- dabs
265
- };
266
- }
267
- function brushstrokeDataUrl(stroke) {
268
- const { width, height, data } = stroke.image;
269
- return width && height ? pngDataUrl(width, height, data) : "";
270
- }
271
- function brushstrokeToCanvas(stroke, createCanvas) {
272
- const { width, height, data } = stroke.image;
273
- if (!width || !height) return null;
274
- const canvas = createCanvas(width, height);
275
- const ctx = canvas.getContext("2d");
276
- if (!ctx) return null;
277
- const img = ctx.createImageData(width, height);
278
- img.data.set(data);
279
- ctx.putImageData(img, 0, 0);
280
- return canvas;
281
- }
282
- function hashString(s, h = 2166136261) {
283
- for (let i = 0; i < s.length; i++) {
284
- h ^= s.charCodeAt(i);
285
- h = Math.imul(h, 16777619) >>> 0;
286
- }
287
- return h;
288
- }
289
- function brushstrokeKey(input, brush, options = {}) {
290
- const path = JSON.stringify(input);
291
- const { brush: tip, ...rest } = brushstrokeStrokeOptions(brush);
292
- const rest2 = JSON.stringify([tip, rest, options.paint ?? options.color ?? null, options.seed ?? 0, options.closed ?? false, options.maxPixels ?? null]);
293
- const scale = finite(options.scale) ? options.scale : 1;
294
- return `${hashString(path).toString(16)}:${path.length}:${hashString(rest2).toString(16)}:${scale}`;
295
- }
296
- var BrushstrokeCache = class {
297
- constructor(capacity = 64) {
298
- this.capacity = capacity;
299
- }
300
- capacity;
301
- entries = /* @__PURE__ */ new Map();
302
- /** Renders that missed the cache (for tests and stats). */
303
- misses = 0;
304
- get size() {
305
- return this.entries.size;
306
- }
307
- /** The cached stroke for these arguments, rendering (and caching) it on a miss. */
308
- render(input, brush, options = {}) {
309
- const key = brushstrokeKey(input, brush, options);
310
- const hit = this.entries.get(key);
311
- if (hit) {
312
- this.entries.delete(key);
313
- this.entries.set(key, hit);
314
- return hit;
315
- }
316
- this.misses++;
317
- const out = renderBrushstroke(input, brush, options);
318
- this.entries.set(key, out);
319
- while (this.entries.size > Math.max(1, this.capacity)) this.entries.delete(this.entries.keys().next().value);
320
- return out;
321
- }
322
- clear() {
323
- this.entries.clear();
324
- }
325
- };
326
-
327
- export {
328
- pathStrokeDabs,
329
- pathStrokeCoverage,
330
- paintPathStroke,
331
- brushstrokeSamples,
332
- brushstrokeStrokeOptions,
333
- isBrushstrokeBrush,
334
- renderBrushstroke,
335
- brushstrokeDataUrl,
336
- brushstrokeToCanvas,
337
- brushstrokeKey,
338
- BrushstrokeCache
339
- };
340
- //# sourceMappingURL=chunk-64PYNEHJ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/paint/path-stroke.ts","../src/paint/brushstroke.ts"],"sourcesContent":["/**\n * A brush stroke along a path: polyline samples, each with an optional pressure and pen tilt, are\n * walked at the brush's spacing (pressure and tilt interpolated between samples) into dabs, built\n * up in a coverage map and composited onto a layer. This is what a vector editor calls to draw a\n * path with a natural-media brush (\"brushstrokes on vector paths\"): the path is the data, the\n * pixels are rendered from it.\n *\n * - Pressure scales size and / or opacity as the brush says.\n * - Tilt (`tiltX` / `tiltY` in degrees, or `altitude` / `azimuth` in radians, as Pointer Events give\n * them) widens the tip (`tilt.size`), flattens it along the lean (`tilt.roundness`), and with\n * `tilt.followAzimuth` turns the tip to the lean, as a pen laid on its side does.\n * - With `followPath` the tip turns with the path (a calligraphic nib along a curve).\n *\n * Deterministic and DOM-free: the same samples and brush give the same pixels everywhere.\n */\n\nimport { applyStrokeToTile, CoverageMap, dabRect, gradientDabColor, normalizeBrush, rasterizeDab, TIP_GRADIENT_STEPS, type BrushSettings, type Dab } from './brush';\nimport { hexToPaintRgb, PaintGradientSampler, resolvePaintGradient, type PaintGradientSample, type PaintSource } from './gradient-paint';\nimport { TiledSurface, finishTile, tileKey, tilesInRect, TILE, type RasterRect } from '../raster/surface';\nimport type { BlendMode } from '../raster/blend';\n\n/** One sample of the path (document pixels). */\nexport interface PathSample {\n x: number;\n y: number;\n /** 0..1 (default 1). */\n pressure?: number;\n /** Pointer Events tilt in degrees (−90..90). */\n tiltX?: number;\n tiltY?: number;\n /** Radians above the surface (π/2 upright); overrides tiltX / tiltY. */\n altitude?: number;\n /** Radians clockwise from +x: where the pen leans (with altitude). */\n azimuth?: number;\n /** Milliseconds (gradient paint mapped by speed or time reads it). */\n t?: number;\n}\n\nexport interface PathStrokeOptions {\n brush: Partial<BrushSettings>;\n /** How tilt shapes the tip (each 0..1; default none). */\n tilt?: { size?: number; roundness?: number; followAzimuth?: boolean };\n /** Turn the tip with the direction of the path (added to the brush angle). */\n followPath?: boolean;\n /** Size at zero pressure as a fraction of the full size, when pressure controls size (default 0). */\n minSize?: number;\n /** Join the last sample back to the first. */\n closed?: boolean;\n}\n\n/** A pen's lean from a sample: 0 upright .. 1 flat, and the direction it leans (radians, clockwise from +x). */\nfunction lean(s: PathSample): { amount: number; azimuth: number } | null {\n if (typeof s.altitude === 'number' && Number.isFinite(s.altitude)) {\n return { amount: 1 - Math.min(1, Math.max(0, s.altitude / (Math.PI / 2))), azimuth: s.azimuth ?? 0 };\n }\n if (typeof s.tiltX === 'number' || typeof s.tiltY === 'number') {\n const tx = ((s.tiltX ?? 0) * Math.PI) / 180;\n const ty = ((s.tiltY ?? 0) * Math.PI) / 180;\n if (tx === 0 && ty === 0) return { amount: 0, azimuth: 0 };\n const az = Math.atan2(Math.tan(ty), Math.tan(tx));\n const alt = Math.atan(1 / Math.hypot(Math.tan(tx), Math.tan(ty)));\n return { amount: 1 - alt / (Math.PI / 2), azimuth: az };\n }\n return null;\n}\n\n/** The dabs of a brush stroke along `samples`, with pressure and tilt interpolated between them. */\nexport function pathStrokeDabs(samples: readonly PathSample[], options: PathStrokeOptions): Dab[] {\n const brush = normalizeBrush(options.brush);\n const pts = options.closed && samples.length > 2 ? [...samples, samples[0]] : [...samples];\n if (!pts.length) return [];\n const tilt = options.tilt ?? {};\n const minSize = Math.min(1, Math.max(0, options.minSize ?? 0));\n let index = 0;\n const t0 = pts.find((s) => typeof s.t === 'number' && Number.isFinite(s.t))?.t ?? null;\n const dab = (x: number, y: number, p: number, l: { amount: number; azimuth: number } | null, dir: number, travel = 0, time: number | null = null, speed: number | null = null): Dab => {\n const pr = Math.min(1, Math.max(0, p));\n const lean0 = l?.amount ?? 0;\n const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * pr : 1) * (1 + 1.5 * (tilt.size ?? 0) * lean0);\n let angle = (brush.angle * Math.PI) / 180;\n if (options.followPath) angle += dir;\n if (tilt.followAzimuth && l) angle = -l.azimuth;\n const roundness = Math.max(0.05, brush.roundness * (1 - 0.85 * (tilt.roundness ?? 0) * lean0));\n const op = brush.pressureOpacity ? pr : 1;\n const params = { travel, index: index++, pressure: pr, lean: lean0, speed, direction: dir, time: time === null || t0 === null ? null : time - t0 };\n return { x, y, radius: Math.max(0.5, size / 2), alpha: brush.flow, opacity: brush.opacity * op, angle, roundness, params };\n };\n const out: Dab[] = [];\n const pressure = (s: PathSample) => (typeof s.pressure === 'number' && Number.isFinite(s.pressure) ? s.pressure : 1);\n if (pts.length === 1) {\n out.push(dab(pts[0].x, pts[0].y, pressure(pts[0]), lean(pts[0]), 0));\n return out;\n }\n let carry = 0;\n let along = 0;\n const timeOf = (s: PathSample) => (typeof s.t === 'number' && Number.isFinite(s.t) ? s.t : null);\n 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])));\n for (let i = 0; i + 1 < pts.length; i++) {\n const a = pts[i];\n const b = pts[i + 1];\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len = Math.hypot(dx, dy);\n if (len === 0) continue;\n // Dab angles are counter-clockwise with y up, as the shared rasterizer reads them.\n const dir = Math.atan2(-dy, dx);\n const la = lean(a);\n const lb = lean(b);\n const ta = timeOf(a);\n const tb = timeOf(b);\n const speed = ta !== null && tb !== null && tb > ta ? len / (tb - ta) : null;\n let t = carry;\n for (;;) {\n const f = t / len;\n if (f > 1) break;\n const p = pressure(a) + (pressure(b) - pressure(a)) * f;\n const l = la && lb ? { amount: la.amount + (lb.amount - la.amount) * f, azimuth: la.azimuth + (lb.azimuth - la.azimuth) * f } : (la ?? lb);\n const size = brush.size * (brush.pressureSize ? minSize + (1 - minSize) * p : 1);\n const step = Math.max(0.5, size * brush.spacing);\n 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));\n t += step;\n }\n carry = t - len;\n along += len;\n }\n return out;\n}\n\n/**\n * Gradient paint for a path stroke: a `PaintSource` gradient sampled dab by dab (by distance along\n * the path, pressure, tilt, speed, direction, time, across the tip or at random). `paint` /\n * `secondary` stops resolve to `colors` (default: the stroke colour and white).\n */\nexport interface PathStrokePaint {\n paint: PaintSource;\n /** Seed of the `random` mapping (default 0). */\n seed?: number;\n colors?: { paint: string; secondary: string };\n}\n\n/** The coverage of a brush stroke along `samples` (build-up towards each dab's opacity) and its bounds. */\nexport function pathStrokeCoverage(samples: readonly PathSample[], options: PathStrokeOptions & { gradient?: PathStrokePaint | null }): { coverage: CoverageMap; bounds: RasterRect | null; dabs: number } {\n const brush = normalizeBrush(options.brush);\n const coverage = new CoverageMap();\n const dabs = pathStrokeDabs(samples, options);\n const g = options.gradient?.paint.type === 'gradient' ? options.gradient : null;\n const sampler = g ? new PaintGradientSampler(resolvePaintGradient(g.paint as Extract<PaintSource, { type: 'gradient' }>, g.colors ?? { paint: '#000000', secondary: '#ffffff' }), g.seed ?? 0) : null;\n const tipTable = sampler?.acrossTip ? sampler.tipTable(TIP_GRADIENT_STEPS) : null;\n const scratch: { field: Float32Array; sample: PaintGradientSample } = { field: new Float32Array(0), sample: { rgb: [0, 0, 0], alpha: 1 } };\n let buf = new Float32Array(0);\n let bounds: RasterRect | null = null;\n for (const d of dabs) {\n const rect = dabRect(d, brush.hardness);\n const n = rect.width * rect.height;\n if (n <= 0) continue;\n if (buf.length < n) buf = new Float32Array(n);\n const vals = buf.subarray(0, n);\n rasterizeDab(d, brush.hardness, rect, vals);\n if (sampler) {\n const { color, field } = gradientDabColor(sampler, tipTable, d, d.params!, rect, vals, scratch);\n coverage.accumulate(rect, vals, d.opacity, color, field);\n } else coverage.accumulate(rect, vals, d.opacity);\n if (!bounds) bounds = { ...rect };\n else {\n const x = Math.min(bounds.x, rect.x);\n const y = Math.min(bounds.y, rect.y);\n 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 };\n }\n }\n return { coverage, bounds, dabs: dabs.length };\n}\n\nexport interface PaintPathOptions extends PathStrokeOptions {\n /** RGBA 0..255 of the paint (with gradient paint: its alpha, and the colour of `paint` stops). */\n color: readonly [number, number, number, number];\n /** Gradient paint (a `PaintSource`); a colour source replaces `color`'s RGB. */\n paint?: PathStrokePaint | null;\n /** `erase` removes paint instead (default `paint`). */\n mode?: 'paint' | 'erase';\n /** Blend mode (default the brush's). */\n blend?: BlendMode;\n /** Layer offset: layer pixel = document pixel − offset (default 0, 0). */\n offset?: { x: number; y: number };\n lockTransparency?: boolean;\n /** Where paint may land (canvas-sized coverage 0..255), or none. */\n selection?: { width: number; height: number; data: ArrayLike<number> } | null;\n}\n\n/**\n * Paints a brush stroke along `samples` onto an RGBA layer surface and returns the new surface\n * (the input is not changed; untouched tiles are shared) with the bounds it touched.\n */\nexport function paintPathStroke(surface: TiledSurface, samples: readonly PathSample[], options: PaintPathOptions): { surface: TiledSurface; bounds: RasterRect | null; dabs: number } {\n if (surface.channels !== 4) throw new Error('paintPathStroke paints RGBA surfaces (4 channels).');\n const brush = normalizeBrush(options.brush);\n const hex = (c: readonly number[]) => '#' + [0, 1, 2].map((i) => Math.round(Math.min(255, Math.max(0, c[i]))).toString(16).padStart(2, '0')).join('');\n let color = options.color;\n if (options.paint?.paint.type === 'color') {\n const rgb = hexToPaintRgb(options.paint.paint.color).map((v) => Math.round(v * 255));\n color = [rgb[0], rgb[1], rgb[2], color[3]];\n }\n const gradient = options.paint?.paint.type === 'gradient' ? { ...options.paint, colors: options.paint.colors ?? { paint: hex(color), secondary: '#ffffff' } } : null;\n const { coverage, bounds, dabs } = pathStrokeCoverage(samples, { ...options, gradient });\n if (!bounds) return { surface, bounds: null, dabs };\n const offset = options.offset ?? { x: 0, y: 0 };\n const apply = {\n mode: options.mode ?? 'paint',\n color,\n background: [0, 0, 0, 0] as const,\n opacity: brush.opacity,\n blend: options.blend ?? brush.blend,\n lockTransparency: options.lockTransparency ?? false,\n selection: options.selection ?? null,\n offset,\n };\n const tiles = new Map(surface.tiles);\n for (const { tx, ty } of tilesInRect({ x: bounds.x - offset.x, y: bounds.y - offset.y, width: bounds.width, height: bounds.height })) {\n const key = tileKey(tx, ty);\n const src = surface.tiles.get(key)?.data;\n const base = src && src.length === TILE * TILE * 4 ? src : new Uint8ClampedArray(TILE * TILE * 4);\n const t = finishTile(applyStrokeToTile(coverage, base, 4, tx, ty, apply), 4, surface.defaultValue);\n if (t) tiles.set(key, t);\n else tiles.delete(key);\n }\n return { surface: new TiledSurface(4, tiles, surface.defaultValue), bounds, dabs };\n}\n","/**\n * A brushstroke rendered on its own: a path (polyline samples with pressure, tilt and time, or\n * points plus pressure / tilt profiles along the length) and a dry brush in, an RGBA image and its\n * bounds out. The canvas renders brushed freehand strokes with it, and the vector editor\n * brushstrokes on vector paths; both keep the path as the data and the pixels as a cache.\n *\n * - Built on {@link paintPathStroke}: the same dabs, coverage and build-up as everywhere else.\n * - Rendered at `scale` image pixels per path unit (both the samples and the brush size scale), so\n * a stroke looks the same at any zoom with more or fewer pixels. Snap zooms to\n * {@link brushstrokeScaleBucket} so panning and small zoom changes reuse the same image.\n * - Dry media only. A wet brush needs an area to flow in (the paint editor's wet layer); mixer,\n * blender and smudge brushes need paint under them. Those are refused with an error.\n * - Deterministic and DOM-free: the image is plain RGBA bytes. Turn it into a PNG data URL with\n * {@link brushstrokeDataUrl}, or into a canvas through an injected factory\n * ({@link brushstrokeToCanvas}).\n */\n\nimport { normalizeBrush, type BrushSettings } from './brush';\nimport { paintPathStroke, type PathSample, type PathStrokeOptions } from './path-stroke';\nimport { scalePaintGradient, type PaintSource } from './gradient-paint';\nimport { emptySurface, readRegion, type RasterRect } from '../raster/surface';\nimport { pngDataUrl } from '../raster/png';\nimport { BRUSHSTROKE_MAX_SCALE as MAX_SCALE, BRUSHSTROKE_MIN_SCALE as MIN_SCALE } from './brushstroke-scale';\n\nexport { brushstrokeScaleBucket, BRUSHSTROKE_MAX_SCALE, BRUSHSTROKE_MIN_SCALE } from './brushstroke-scale';\n\n/** One sample of a brushstroke: a {@link PathSample} plus an optional time stamp (milliseconds). */\nexport interface BrushstrokeSample extends PathSample {\n t?: number;\n}\n\n/**\n * A path plus pen profiles along it. Each profile is a list of values spread evenly over the\n * path's length (first at the start, last at the end) and interpolated in between; one value is\n * constant. Missing profiles leave the samples upright at full pressure.\n */\nexport interface BrushstrokeProfilePath {\n points: ReadonlyArray<{ x: number; y: number }>;\n /** Pressure 0..1 along the length. */\n pressure?: readonly number[];\n /** Pointer Events tilt in degrees (−90..90) along the length. */\n tiltX?: readonly number[];\n tiltY?: readonly number[];\n}\n\nexport type BrushstrokeInput = readonly BrushstrokeSample[] | BrushstrokeProfilePath;\n\n/**\n * A brush for a brushstroke: plain tip settings, or a paint-editor brush (`PaintBrush` matches this\n * shape structurally). Only dab-engine brushes render; `rotation: 'stroke'` turns the tip with the\n * path, and `dynamics` give the size at zero pressure and how tilt shapes the tip. Grain, jitter and\n * velocity dynamics are not applied (there is no paper, and the result must be deterministic).\n */\nexport type BrushstrokeBrush =\n | Partial<BrushSettings>\n | {\n id?: string;\n name?: string;\n engine?: string;\n tip: Partial<BrushSettings>;\n rotation?: string;\n dynamics?: { minSize?: number; tiltSize?: number; tiltRoundness?: number };\n };\n\n/**\n * What the stroke is painted with: a solid RGBA colour, or a `PaintSource` (one `#rrggbb` colour,\n * or gradient paint sampled dab by dab along the path: by distance, pressure, tilt, speed,\n * direction, time, across the tip or at random).\n */\nexport type BrushstrokePaint = { color: readonly [number, number, number, number] } | PaintSource;\n\nexport interface BrushstrokeOptions {\n /** Image pixels per path unit (default 1). Use {@link brushstrokeScaleBucket} for a zoom. */\n scale?: number;\n /** RGBA 0..255 (default opaque black). Shorthand for `paint: { color }`. */\n color?: readonly [number, number, number, number];\n /** What to paint with (wins over `color`; with gradient paint, `color` fills `paint` stops and gives the alpha). */\n paint?: BrushstrokePaint;\n /** Seed of gradient paint's `random` mapping (default 0). */\n seed?: number;\n /** Join the last sample back to the first. */\n closed?: boolean;\n /**\n * The largest image to render, in pixels (default 16 777 216, 4096²). A stroke that would be\n * bigger at `scale` renders at a lower scale instead; the result's `scale` says which.\n */\n maxPixels?: number;\n}\n\n/** A rendered brushstroke. */\nexport interface BrushstrokeImage {\n /** Straight-alpha RGBA bytes, `width × height × 4` (the same layout as `ImageData`). */\n image: { width: number; height: number; data: Uint8ClampedArray };\n /** Where the image goes, in path units: draw `image` stretched over this rectangle. Null for an empty stroke. */\n bounds: { x: number; y: number; width: number; height: number } | null;\n /** Image pixels per path unit it was rendered at. */\n scale: number;\n /** How many dabs the stroke laid down. */\n dabs: number;\n}\n\nconst DEFAULT_MAX_PIXELS = 4096 * 4096;\n\nconst isProfilePath = (input: BrushstrokeInput): input is BrushstrokeProfilePath => !Array.isArray(input) && typeof input === 'object' && input !== null && 'points' in input;\n\n/** A profile's value at `f` (0..1 along the length), or undefined without one. */\nfunction profileAt(profile: readonly number[] | undefined, f: number): number | undefined {\n if (!profile || !profile.length) return undefined;\n if (profile.length === 1) return profile[0];\n const u = Math.min(1, Math.max(0, f)) * (profile.length - 1);\n const i = Math.min(profile.length - 2, Math.floor(u));\n return profile[i] + (profile[i + 1] - profile[i]) * (u - i);\n}\n\n/** The samples of a brushstroke input: profile paths are sampled at their points by arc length. */\nexport function brushstrokeSamples(input: BrushstrokeInput): BrushstrokeSample[] {\n if (!isProfilePath(input)) return input.map((s) => ({ ...s }));\n const pts = input.points;\n const lengths = [0];\n 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));\n const total = lengths.at(-1) ?? 0;\n return pts.map((p, i) => {\n const f = total > 0 ? lengths[i] / total : pts.length > 1 ? i / (pts.length - 1) : 0;\n const s: BrushstrokeSample = { x: p.x, y: p.y };\n const pressure = profileAt(input.pressure, f);\n const tiltX = profileAt(input.tiltX, f);\n const tiltY = profileAt(input.tiltY, f);\n if (pressure !== undefined) s.pressure = pressure;\n if (tiltX !== undefined) s.tiltX = tiltX;\n if (tiltY !== undefined) s.tiltY = tiltY;\n return s;\n });\n}\n\n/** Engines that cannot render a brushstroke on its own, and why. */\nconst REFUSED_ENGINES: Record<string, string> = {\n wet: 'is a wet brush, and wet paint needs an area to flow in (paint it on a wet layer in the paint editor)',\n mixer: 'is a Mixer Brush, which picks up paint from under the tip and has none on a standalone stroke',\n blender: 'is a blender, which only moves paint that is already there',\n smudge: 'is a smudge brush, which only moves paint that is already there',\n eraser: 'is an eraser, which has nothing to remove on a standalone stroke',\n};\n\n/**\n * The tip settings and path-stroke options of a brushstroke brush. Throws for brushes that are not\n * dry dab brushes (wet, mixer, blender, smudge, eraser, impasto and the procedural engines).\n */\nexport function brushstrokeStrokeOptions(brush: BrushstrokeBrush): PathStrokeOptions {\n if (!brush || typeof brush !== 'object') throw new Error('A brushstroke needs a brush: tip settings ({ size, hardness, … }) or a paint brush preset.');\n if (!('tip' in brush) || !brush.tip) return { brush: normalizeBrush(brush as Partial<BrushSettings>) };\n const engine = brush.engine ?? 'dab';\n if (engine !== 'dab') {\n const label = brush.name ?? brush.id ?? 'This brush';\n const why = REFUSED_ENGINES[engine] ?? `uses the '${engine}' engine, which renderBrushstroke does not draw`;\n 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.`);\n }\n const d = brush.dynamics ?? {};\n const tilt = { size: d.tiltSize ?? 0, roundness: d.tiltRoundness ?? 0 };\n return {\n brush: normalizeBrush(brush.tip),\n followPath: brush.rotation === 'stroke',\n minSize: d.minSize ?? 0,\n ...(tilt.size || tilt.roundness ? { tilt } : {}),\n };\n}\n\n/** Whether a brush renders as a brushstroke (a dry dab brush, or plain tip settings). */\nexport function isBrushstrokeBrush(brush: BrushstrokeBrush): boolean {\n return !('tip' in brush) || !brush.tip || (brush.engine ?? 'dab') === 'dab';\n}\n\nconst finite = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);\n\n/**\n * Renders a dry brushstroke along `input` with `brush` to an RGBA image. The same input, brush and\n * options always give the same bytes. Throws for a wet (or other non-dry) brush and for samples\n * that are not finite.\n */\nexport function renderBrushstroke(input: BrushstrokeInput, brush: BrushstrokeBrush, options: BrushstrokeOptions = {}): BrushstrokeImage {\n const strokeOptions = brushstrokeStrokeOptions(brush);\n const samples = brushstrokeSamples(input);\n for (const s of samples) if (!finite(s.x) || !finite(s.y)) throw new Error('Brushstroke samples need finite x and y.');\n const solid = options.paint && !('type' in options.paint) ? options.paint.color : null;\n const source = options.paint && 'type' in options.paint ? options.paint : null;\n const color = solid ?? options.color ?? [0, 0, 0, 255];\n let scale = finite(options.scale) && options.scale > 0 ? Math.min(MAX_SCALE, Math.max(MIN_SCALE, options.scale)) : 1;\n if (!samples.length) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs: 0 };\n\n // Keep the image under maxPixels: estimate it from the samples and the widest the tip can get.\n const tip = strokeOptions.brush as BrushSettings;\n const reach = tip.size * (1 + 1.5 * (strokeOptions.tilt?.size ?? 0)) + 4;\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n for (const s of samples) {\n x0 = Math.min(x0, s.x); y0 = Math.min(y0, s.y); x1 = Math.max(x1, s.x); y1 = Math.max(y1, s.y);\n }\n const maxPixels = finite(options.maxPixels) && options.maxPixels > 0 ? options.maxPixels : DEFAULT_MAX_PIXELS;\n const area = (x1 - x0 + reach) * (y1 - y0 + reach);\n if (area * scale * scale > maxPixels) scale = Math.max(MIN_SCALE, Math.sqrt(maxPixels / area));\n\n const scaled = samples.map((s) => ({ ...s, x: s.x * scale, y: s.y * scale }));\n const { surface, bounds, dabs } = paintPathStroke(emptySurface(4), scaled, {\n ...strokeOptions,\n brush: { ...tip, size: tip.size * scale },\n closed: options.closed ?? strokeOptions.closed,\n color,\n // Gradient lengths are in path units: scale them with the image.\n ...(source ? { paint: { paint: source.type === 'gradient' ? scalePaintGradient(source, scale) : source, seed: options.seed ?? 0 } } : {}),\n });\n if (!bounds) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs };\n const rect: RasterRect = bounds;\n const data = readRegion(surface, rect);\n return {\n image: { width: rect.width, height: rect.height, data },\n bounds: { x: rect.x / scale, y: rect.y / scale, width: rect.width / scale, height: rect.height / scale },\n scale,\n dabs,\n };\n}\n\n/** A rendered brushstroke as a PNG data URL (for an SVG `<image>`, an `<img>` or an export). Empty strokes give ''. */\nexport function brushstrokeDataUrl(stroke: BrushstrokeImage): string {\n const { width, height, data } = stroke.image;\n return width && height ? pngDataUrl(width, height, data) : '';\n}\n\n/** The 2D-canvas surface {@link brushstrokeToCanvas} draws on (an `HTMLCanvasElement` or `OffscreenCanvas`). */\nexport interface BrushstrokeCanvasLike {\n getContext(type: '2d'): { createImageData(w: number, h: number): { data: Uint8ClampedArray }; putImageData(img: never, x: number, y: number): void } | null;\n}\n\n/**\n * A rendered brushstroke on a canvas made by `createCanvas(width, height)` (injected, so this\n * module stays DOM-free). Returns null for an empty stroke or a canvas without a 2D context.\n */\nexport function brushstrokeToCanvas<C extends BrushstrokeCanvasLike>(stroke: BrushstrokeImage, createCanvas: (width: number, height: number) => C): C | null {\n const { width, height, data } = stroke.image;\n if (!width || !height) return null;\n const canvas = createCanvas(width, height);\n const ctx = canvas.getContext('2d');\n if (!ctx) return null;\n const img = ctx.createImageData(width, height);\n img.data.set(data);\n ctx.putImageData(img as never, 0, 0);\n return canvas;\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\n/** FNV-1a over a string, as 8 hex digits. */\nfunction hashString(s: string, h = 0x811c9dc5): number {\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h;\n}\n\n/**\n * A key for a brushstroke: the path (samples or profiles), the brush, the colour and the scale.\n * Two calls with equal arguments give equal keys; any change to them changes the key.\n */\nexport function brushstrokeKey(input: BrushstrokeInput, brush: BrushstrokeBrush, options: BrushstrokeOptions = {}): string {\n const path = JSON.stringify(input);\n const { brush: tip, ...rest } = brushstrokeStrokeOptions(brush);\n const rest2 = JSON.stringify([tip, rest, options.paint ?? options.color ?? null, options.seed ?? 0, options.closed ?? false, options.maxPixels ?? null]);\n const scale = finite(options.scale) ? options.scale : 1;\n return `${hashString(path).toString(16)}:${path.length}:${hashString(rest2).toString(16)}:${scale}`;\n}\n\n/**\n * A small least-recently-used cache of rendered brushstrokes keyed by {@link brushstrokeKey}: path,\n * brush, colour and scale. Pass a bucketed scale ({@link brushstrokeScaleBucket}) so zooming inside\n * a bucket hits the cache, and a changed path, brush or bucket re-renders.\n */\nexport class BrushstrokeCache {\n private readonly entries = new Map<string, BrushstrokeImage>();\n /** Renders that missed the cache (for tests and stats). */\n misses = 0;\n\n constructor(readonly capacity = 64) {}\n\n get size(): number {\n return this.entries.size;\n }\n\n /** The cached stroke for these arguments, rendering (and caching) it on a miss. */\n render(input: BrushstrokeInput, brush: BrushstrokeBrush, options: BrushstrokeOptions = {}): BrushstrokeImage {\n const key = brushstrokeKey(input, brush, options);\n const hit = this.entries.get(key);\n if (hit) {\n this.entries.delete(key);\n this.entries.set(key, hit);\n return hit;\n }\n this.misses++;\n const out = renderBrushstroke(input, brush, options);\n this.entries.set(key, out);\n while (this.entries.size > Math.max(1, this.capacity)) this.entries.delete(this.entries.keys().next().value as string);\n return out;\n }\n\n clear(): void {\n this.entries.clear();\n }\n}\n"],"mappings":"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","names":[]}