@pixerate/editor 0.246.0 → 0.249.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 (105) hide show
  1. package/README.md +1 -0
  2. package/dist/animation-editor/index.cjs +64 -61
  3. package/dist/animation-editor/index.js +8 -5
  4. package/dist/{brushstrokeRender-ZDHMXJAE.cjs → brushstrokeRender-GCKK7V2I.cjs} +14 -11
  5. package/dist/{brushstrokeRender-AKUI3SEJ.js → brushstrokeRender-T3JZST44.js} +7 -4
  6. package/dist/canvas/index.cjs +8 -8
  7. package/dist/canvas/index.js +1 -1
  8. package/dist/{chunk-UVPD55UZ.cjs → chunk-2SMMZDMG.cjs} +35 -35
  9. package/dist/chunk-2VJAVXLI.js +192 -0
  10. package/dist/{chunk-OLNIPTB3.cjs → chunk-2VX55NFM.cjs} +1 -320
  11. package/dist/chunk-3K6HWEXZ.cjs +92 -0
  12. package/dist/{chunk-UOOK7HTX.js → chunk-3PPJRMIG.js} +1 -1
  13. package/dist/chunk-4D6KP5BV.js +92 -0
  14. package/dist/chunk-5RH3VXAK.js +269 -0
  15. package/dist/{chunk-LDPLEZ5T.js → chunk-6BIDVPED.js} +1 -1
  16. package/dist/{chunk-2ONK3ERD.cjs → chunk-75K3W4D6.cjs} +20 -20
  17. package/dist/chunk-BOHDJJ2O.cjs +192 -0
  18. package/dist/{chunk-2PBZBRP5.js → chunk-D5KGAO4Z.js} +10 -92
  19. package/dist/{chunk-DQWPMGH2.js → chunk-DOIU7VJG.js} +1 -1
  20. package/dist/{chunk-HB4ICNNA.cjs → chunk-DPYAO5U6.cjs} +42 -42
  21. package/dist/chunk-EJLRBS5Q.js +301 -0
  22. package/dist/{chunk-Q2UYUWAZ.cjs → chunk-H4AOUUBE.cjs} +679 -164
  23. package/dist/{chunk-PQ3H2AAN.cjs → chunk-I7NJPHWV.cjs} +122 -204
  24. package/dist/{chunk-FK5LTDOO.cjs → chunk-IOFVM3L3.cjs} +7 -7
  25. package/dist/{chunk-42TWUPNJ.js → chunk-IRSTHLQQ.js} +1 -320
  26. package/dist/{chunk-VDAWA546.js → chunk-J7T5QZ45.js} +614 -99
  27. package/dist/{chunk-VMROIWLL.cjs → chunk-KBYB7OWB.cjs} +3 -3
  28. package/dist/{chunk-5MFDD7SP.cjs → chunk-KYX7NZ5V.cjs} +1 -1
  29. package/dist/chunk-LJRCU3BJ.cjs +301 -0
  30. package/dist/{chunk-XTWXBQD4.cjs → chunk-LP2MCSBE.cjs} +593 -236
  31. package/dist/{chunk-A23PSOBB.js → chunk-O75V4ZS3.js} +1 -1
  32. package/dist/chunk-OTTT5N3K.js +322 -0
  33. package/dist/{chunk-N6WUB3Y5.js → chunk-PZP2SZIZ.js} +2 -2
  34. package/dist/{chunk-4PMCMWQS.js → chunk-UBRJOIR5.js} +367 -10
  35. package/dist/chunk-W5M3J7PV.cjs +322 -0
  36. package/dist/{chunk-4P6H35HU.js → chunk-WA44NKPF.js} +2 -2
  37. package/dist/{chunk-CEPKXLBS.cjs → chunk-WLJZZHUZ.cjs} +23 -23
  38. package/dist/{chunk-P3NIPIKS.js → chunk-WQCNC4DK.js} +1 -1
  39. package/dist/chunk-ZEYMKDYS.cjs +269 -0
  40. package/dist/image-editor/effect-tools.d.cts +37 -4
  41. package/dist/image-editor/effect-tools.d.ts +37 -4
  42. package/dist/image-editor/index.cjs +1340 -1828
  43. package/dist/image-editor/index.d.cts +1 -1
  44. package/dist/image-editor/index.d.ts +1 -1
  45. package/dist/image-editor/index.js +298 -786
  46. package/dist/paint/index.cjs +8 -5
  47. package/dist/paint/index.js +13 -10
  48. package/dist/paint-editor/controller.d.cts +71 -2
  49. package/dist/paint-editor/controller.d.ts +71 -2
  50. package/dist/paint-editor/index.cjs +901 -589
  51. package/dist/paint-editor/index.d.cts +3 -0
  52. package/dist/paint-editor/index.d.ts +3 -0
  53. package/dist/paint-editor/index.js +365 -53
  54. package/dist/paint-editor/procedural.d.cts +82 -5
  55. package/dist/paint-editor/procedural.d.ts +82 -5
  56. package/dist/paint-editor/sticker-art.d.cts +55 -0
  57. package/dist/paint-editor/sticker-art.d.ts +55 -0
  58. package/dist/paint-editor/stickers.d.cts +30 -17
  59. package/dist/paint-editor/stickers.d.ts +30 -17
  60. package/dist/paint-editor/stroke.d.cts +4 -0
  61. package/dist/paint-editor/stroke.d.ts +4 -0
  62. package/dist/paint-editor/tools.d.cts +1 -1
  63. package/dist/paint-editor/tools.d.ts +1 -1
  64. package/dist/paint-editor/ui.d.cts +25 -4
  65. package/dist/paint-editor/ui.d.ts +25 -4
  66. package/dist/{image-editor/layers/effects-ui.d.cts → raster/effects/ui.d.cts} +32 -5
  67. package/dist/{image-editor/layers/effects-ui.d.ts → raster/effects/ui.d.ts} +32 -5
  68. package/dist/raster/index.cjs +17 -3
  69. package/dist/raster/index.d.cts +1 -0
  70. package/dist/raster/index.d.ts +1 -0
  71. package/dist/raster/index.js +23 -9
  72. package/dist/stickers/browser.d.cts +48 -0
  73. package/dist/stickers/browser.d.ts +48 -0
  74. package/dist/stickers/combine.d.cts +10 -0
  75. package/dist/stickers/combine.d.ts +10 -0
  76. package/dist/stickers/conformance.d.cts +18 -0
  77. package/dist/stickers/conformance.d.ts +18 -0
  78. package/dist/stickers/editor.d.cts +14 -0
  79. package/dist/stickers/editor.d.ts +14 -0
  80. package/dist/stickers/errors.d.cts +6 -0
  81. package/dist/stickers/errors.d.ts +6 -0
  82. package/dist/stickers/fake.d.cts +16 -0
  83. package/dist/stickers/fake.d.ts +16 -0
  84. package/dist/stickers/helpers.d.cts +51 -0
  85. package/dist/stickers/helpers.d.ts +51 -0
  86. package/dist/stickers/index.cjs +331 -0
  87. package/dist/stickers/index.d.cts +16 -0
  88. package/dist/stickers/index.d.ts +16 -0
  89. package/dist/stickers/index.js +331 -0
  90. package/dist/stickers/stamps.d.cts +13 -0
  91. package/dist/stickers/stamps.d.ts +13 -0
  92. package/dist/stickers/tools.d.cts +25 -0
  93. package/dist/stickers/tools.d.ts +25 -0
  94. package/dist/stickers/types.d.cts +123 -0
  95. package/dist/stickers/types.d.ts +123 -0
  96. package/dist/vector/index.cjs +978 -975
  97. package/dist/vector/index.js +9 -6
  98. package/package.json +11 -1
  99. package/skills/README.md +1 -0
  100. package/skills/pixerate-paint-editor/SKILL.md +26 -32
  101. package/skills/pixerate-paint-editor/references/gradient-paint.md +23 -0
  102. package/skills/pixerate-paint-editor/references/layer-effects.md +28 -0
  103. package/skills/pixerate-paint-editor/references/light-paint.md +10 -2
  104. package/skills/pixerate-paint-editor/references/particle-brushes.md +66 -0
  105. package/skills/pixerate-stickers/SKILL.md +45 -0
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7;
2
2
 
3
3
 
4
4
 
@@ -6,6 +6,7 @@
6
6
 
7
7
 
8
8
 
9
+ var _chunkZEYMKDYScjs = require('./chunk-ZEYMKDYS.cjs');
9
10
 
10
11
 
11
12
 
@@ -20,7 +21,21 @@
20
21
 
21
22
 
22
23
 
23
- var _chunkPQ3H2AANcjs = require('./chunk-PQ3H2AAN.cjs');
24
+
25
+
26
+
27
+
28
+
29
+ var _chunkI7NJPHWVcjs = require('./chunk-I7NJPHWV.cjs');
30
+
31
+
32
+
33
+
34
+ var _chunk3K6HWEXZcjs = require('./chunk-3K6HWEXZ.cjs');
35
+
36
+
37
+
38
+ var _chunkW5M3J7PVcjs = require('./chunk-W5M3J7PV.cjs');
24
39
 
25
40
 
26
41
 
@@ -229,7 +244,7 @@ function normalizePaper(raw = {}, base) {
229
244
  preset: typeof r.preset === "string" && r.preset ? r.preset : b.preset,
230
245
  name: typeof r.name === "string" && r.name.trim() ? r.name.trim() : b.name,
231
246
  family: PAPER_FAMILIES.includes(r.family) ? r.family : b.family,
232
- pattern: _chunkPQ3H2AANcjs.GRAIN_PATTERNS.includes(r.pattern) ? r.pattern : b.pattern,
247
+ pattern: _chunkI7NJPHWVcjs.GRAIN_PATTERNS.includes(r.pattern) ? r.pattern : b.pattern,
233
248
  seed: typeof r.seed === "number" && Number.isFinite(r.seed) ? Math.round(Math.abs(r.seed)) % 2147483647 : b.seed,
234
249
  color: typeof r.color === "string" && HEX.test(r.color) ? r.color.toLowerCase() : b.color,
235
250
  scale: clampNum(r.scale, 0.25, 8, b.scale),
@@ -251,7 +266,7 @@ function hexToRgb8(hex) {
251
266
  const n = parseInt(hex.slice(1, 7), 16);
252
267
  return [n >> 16 & 255, n >> 8 & 255, n & 255];
253
268
  }
254
- function renderPaper(paper, rect, grain = new (0, _chunkPQ3H2AANcjs.PaperGrain)(paper), light) {
269
+ function renderPaper(paper, rect, grain = new (0, _chunkI7NJPHWVcjs.PaperGrain)(paper), light) {
255
270
  const [r, g, b] = hexToRgb8(paper.color);
256
271
  const out = new Uint8ClampedArray(rect.width * rect.height * 4);
257
272
  const shade = grain.flat ? 0 : paper.shading * 0.16;
@@ -413,22 +428,22 @@ var PenDabWalker = (_class2 = class {
413
428
  const i = this.index++;
414
429
  const seed = this.seed;
415
430
  let diameter = this.diameter(s);
416
- if (dyn.sizeJitter) diameter *= Math.max(0.05, 1 + dyn.sizeJitter * _chunkPQ3H2AANcjs.randSigned.call(void 0, seed, i, 1 /* Size */));
431
+ if (dyn.sizeJitter) diameter *= Math.max(0.05, 1 + dyn.sizeJitter * _chunk3K6HWEXZcjs.randSigned.call(void 0, seed, i, 1 /* Size */));
417
432
  const base = tip.angle * Math.PI / 180;
418
433
  let angle;
419
434
  if (rotation === "stroke") angle = this.dir + base;
420
435
  else if (rotation === "barrel" && s.twist !== null) angle = this.mapAngle(base - s.twist * Math.PI / 180);
421
436
  else if (dyn.tiltRoundness > 0 && s.lean > 0.02) angle = this.mapAngle(base - s.azimuth);
422
437
  else angle = this.mapAngle(base);
423
- if (dyn.angleJitter) angle += dyn.angleJitter * Math.PI * _chunkPQ3H2AANcjs.randSigned.call(void 0, seed, i, 2 /* Angle */);
438
+ if (dyn.angleJitter) angle += dyn.angleJitter * Math.PI * _chunk3K6HWEXZcjs.randSigned.call(void 0, seed, i, 2 /* Angle */);
424
439
  let opacity = tip.pressureOpacity ? s.pressure : 1;
425
440
  opacity *= 1 - dyn.tiltOpacity * s.lean * 0.8;
426
441
  if (s.speed !== null && dyn.velocityOpacity) opacity *= Math.max(0, 1 + dyn.velocityOpacity * (Math.min(1, s.speed / FULL_SPEED) - 0.5));
427
- if (dyn.opacityJitter) opacity *= 1 - dyn.opacityJitter * (_chunkPQ3H2AANcjs.randSigned.call(void 0, seed, i, 3 /* Opacity */) * 0.5 + 0.5);
442
+ if (dyn.opacityJitter) opacity *= 1 - dyn.opacityJitter * (_chunk3K6HWEXZcjs.randSigned.call(void 0, seed, i, 3 /* Opacity */) * 0.5 + 0.5);
428
443
  const flow = tip.flow * (1 - dyn.pressureFlow + dyn.pressureFlow * s.pressure);
429
444
  let { x, y } = s;
430
445
  if (dyn.scatter) {
431
- const off = dyn.scatter * diameter * _chunkPQ3H2AANcjs.randSigned.call(void 0, seed, i, 4 /* Scatter */);
446
+ const off = dyn.scatter * diameter * _chunk3K6HWEXZcjs.randSigned.call(void 0, seed, i, 4 /* Scatter */);
432
447
  x += Math.sin(this.dir) * off;
433
448
  y += Math.cos(this.dir) * off;
434
449
  }
@@ -518,7 +533,7 @@ var DryStroke = (_class3 = class {
518
533
  }
519
534
 
520
535
 
521
- __init12() {this.coverage = new (0, _chunkPQ3H2AANcjs.CoverageMap)()}
536
+ __init12() {this.coverage = new (0, _chunkI7NJPHWVcjs.CoverageMap)()}
522
537
  __init13() {this.dabCount = 0}
523
538
 
524
539
 
@@ -553,16 +568,16 @@ var DryStroke = (_class3 = class {
553
568
  const hardness = this.brush.tip.hardness;
554
569
  const grain = this.options.grain;
555
570
  for (const d of dabs) {
556
- const rect = _chunkPQ3H2AANcjs.dabRect.call(void 0, d, hardness);
571
+ const rect = _chunkI7NJPHWVcjs.dabRect.call(void 0, d, hardness);
557
572
  const n = rect.width * rect.height;
558
573
  if (this.buf.length < n) this.buf = new Float32Array(n);
559
574
  const vals = this.buf.subarray(0, n);
560
- _chunkPQ3H2AANcjs.rasterizeDab.call(void 0, d, hardness, rect, vals);
575
+ _chunkI7NJPHWVcjs.rasterizeDab.call(void 0, d, hardness, rect, vals);
561
576
  if (this.depth > 0 && grain) {
562
577
  for (let yy = 0, k = 0; yy < rect.height; yy++) {
563
578
  const y = rect.y + yy + 0.5;
564
579
  for (let xx = 0; xx < rect.width; xx++, k++) {
565
- if (vals[k] > 0) vals[k] *= _chunkPQ3H2AANcjs.grainCatch.call(void 0, grain.at(rect.x + xx + 0.5, y), this.depth, d.pressure);
580
+ if (vals[k] > 0) vals[k] *= _chunkI7NJPHWVcjs.grainCatch.call(void 0, grain.at(rect.x + xx + 0.5, y), this.depth, d.pressure);
566
581
  }
567
582
  }
568
583
  }
@@ -586,14 +601,129 @@ function toStrokePoint(p) {
586
601
  return { x: p.x, y: p.y, pressure: pressureOf(p), ...typeof p.t === "number" ? { t: p.t } : {} };
587
602
  }
588
603
 
604
+ // src/paint-editor/sticker-art.ts
605
+ var STICKER_ART_SIZE = 256;
606
+ var defaultStickerRasterizer = async (asset, size) => {
607
+ if (asset.kind === "image") {
608
+ const parsed = _chunkW5M3J7PVcjs.parseDataUrl.call(void 0, asset.url);
609
+ if (parsed && parsed.mime === "image/png") {
610
+ const png = _chunkW5M3J7PVcjs.decodePng.call(void 0, parsed.bytes);
611
+ return { width: png.width, height: png.height, data: new Uint8ClampedArray(png.data) };
612
+ }
613
+ }
614
+ const g = globalThis;
615
+ if (!g.createImageBitmap || !g.OffscreenCanvas) {
616
+ throw new (0, _chunkZEYMKDYScjs.StickerError)("unsupported_format", `This environment can't draw ${asset.kind === "svg" ? "SVG" : "non-PNG"} sticker art; pass rasterizeSticker in the paint editor's options (or use a provider that offers png).`);
617
+ }
618
+ const blob = asset.kind === "svg" ? new Blob([asset.svg], { type: "image/svg+xml" }) : await (await g.fetch(asset.url)).blob();
619
+ const bitmap = await g.createImageBitmap(blob);
620
+ const scale = size / Math.max(bitmap.width, bitmap.height, 1);
621
+ const w = Math.max(1, Math.round(bitmap.width * scale));
622
+ const h = Math.max(1, Math.round(bitmap.height * scale));
623
+ const canvas = new g.OffscreenCanvas(w, h);
624
+ const ctx = canvas.getContext("2d");
625
+ ctx.drawImage(bitmap, 0, 0, w, h);
626
+ const img = ctx.getImageData(0, 0, w, h);
627
+ return { width: w, height: h, data: img.data };
628
+ };
629
+ var StickerArtCache = (_class4 = class {constructor() { _class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this); }
630
+ __init18() {this.entries = /* @__PURE__ */ new Map()}
631
+ /** The art for `ref` when it has loaded (editor stamps never need any). */
632
+ __init19() {this.get = (ref) => {
633
+ const e = this.entries.get(ref);
634
+ return _optionalChain([e, 'optionalAccess', _8 => _8.status]) === "ready" ? e.art : void 0;
635
+ }}
636
+ /** `ready`, `loading`, `failed` (with the reason) or `none` (never asked for). Editor stamps are always `ready`. */
637
+ status(ref) {
638
+ if (_chunkZEYMKDYScjs.editorStampOf.call(void 0, ref)) return { status: "ready" };
639
+ const e = this.entries.get(ref);
640
+ if (!e) return { status: "none" };
641
+ return e.status === "failed" ? { status: "failed", error: e.error } : { status: e.status };
642
+ }
643
+ /**
644
+ * Loads and rasterises `ref` from `provider` once (later calls share the load). Unknown refs
645
+ * reject with the closest known ones; failures are kept so they are reported, and retried on
646
+ * the next call.
647
+ */
648
+ ensure(provider, ref, rasterize) {
649
+ if (_chunkZEYMKDYScjs.editorStampOf.call(void 0, ref)) return Promise.resolve(null);
650
+ const e = this.entries.get(ref);
651
+ if (_optionalChain([e, 'optionalAccess', _9 => _9.status]) === "ready") return Promise.resolve(e.art);
652
+ if (_optionalChain([e, 'optionalAccess', _10 => _10.status]) === "loading") return e.promise;
653
+ const promise = (async () => {
654
+ const d = await _chunkZEYMKDYScjs.describeStickerRef.call(void 0, provider, ref);
655
+ const id = _chunkZEYMKDYScjs.stickerIdFor.call(void 0, provider, ref);
656
+ const max = _nullishCoalesce(provider.capabilities.maxSize, () => ( STICKER_ART_SIZE));
657
+ const size = Math.min(STICKER_ART_SIZE, max);
658
+ const asset = await provider.load(id, d.formats.includes("png") ? { format: "png", size } : { format: _nullishCoalesce(d.formats[0], () => ( "svg")), size });
659
+ const px = await rasterize(asset, size);
660
+ if (!(px.width > 0 && px.height > 0) || px.data.length !== px.width * px.height * 4) throw new (0, _chunkZEYMKDYScjs.StickerError)("load_failed", `The art for "${ref}" came back empty or the wrong size.`);
661
+ return { ...px, tintable: d.tintable };
662
+ })();
663
+ this.entries.set(ref, { status: "loading", promise });
664
+ return promise.then(
665
+ (art) => {
666
+ this.entries.set(ref, { status: "ready", art });
667
+ return art;
668
+ },
669
+ (err) => {
670
+ const message = err instanceof Error ? err.message : String(err);
671
+ this.entries.set(ref, { status: "failed", error: message });
672
+ throw err;
673
+ }
674
+ );
675
+ }
676
+ /** Forgets every load (a new provider). */
677
+ clear() {
678
+ this.entries.clear();
679
+ }
680
+ }, _class4);
681
+ function sampleArt(art, x, y, out) {
682
+ const { width: w, height: h, data } = art;
683
+ const fx = x - 0.5;
684
+ const fy = y - 0.5;
685
+ const x0 = Math.floor(fx);
686
+ const y0 = Math.floor(fy);
687
+ const tx = fx - x0;
688
+ const ty = fy - y0;
689
+ let r = 0;
690
+ let g = 0;
691
+ let b = 0;
692
+ let a = 0;
693
+ for (let k = 0; k < 4; k++) {
694
+ const xx = x0 + (k & 1);
695
+ const yy = y0 + (k >> 1);
696
+ if (xx < 0 || yy < 0 || xx >= w || yy >= h) continue;
697
+ const wgt = (k & 1 ? tx : 1 - tx) * (k >> 1 ? ty : 1 - ty);
698
+ const i = (yy * w + xx) * 4;
699
+ const al = data[i + 3] * wgt;
700
+ r += data[i] * al;
701
+ g += data[i + 1] * al;
702
+ b += data[i + 2] * al;
703
+ a += al;
704
+ }
705
+ out[0] = a > 0 ? r / a / 255 : 0;
706
+ out[1] = a > 0 ? g / a / 255 : 0;
707
+ out[2] = a > 0 ? b / a / 255 : 0;
708
+ out[3] = a / 255;
709
+ return out;
710
+ }
711
+
589
712
  // src/paint-editor/stickers.ts
590
- var STAMP_KINDS = ["dot", "star", "heart", "leaf", "flower", "confetti", "splat", "sparkle", "ring"];
591
- var DEFAULT_STICKER = { stamps: ["star"], density: 0.4, sizeJitter: 0.4, rotationJitter: 1, scatter: 0.5, colorJitter: 0.2 };
713
+ var MAX_STICKER_STAMPS = 16;
714
+ var DEFAULT_STICKER = { stamps: ["editor:star"], density: 0.4, sizeJitter: 0.4, rotationJitter: 1, scatter: 0.5, colorJitter: 0.2 };
592
715
  var unit2 = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
593
716
  function normalizeStickerSettings(raw = {}, base = DEFAULT_STICKER) {
594
- const stamps = Array.isArray(raw.stamps) ? raw.stamps.filter((s) => STAMP_KINDS.includes(s)) : base.stamps;
717
+ const refs = (list) => list.flatMap((s) => {
718
+ try {
719
+ return [_chunkZEYMKDYScjs.normalizeStickerRef.call(void 0, s)];
720
+ } catch (e3) {
721
+ return [];
722
+ }
723
+ });
724
+ const stamps = Array.isArray(raw.stamps) ? refs(raw.stamps) : refs(base.stamps);
595
725
  return {
596
- stamps: stamps.length ? [...new Set(stamps)] : [...base.stamps],
726
+ stamps: stamps.length ? [...new Set(stamps)].slice(0, MAX_STICKER_STAMPS) : refs(base.stamps.length ? base.stamps : DEFAULT_STICKER.stamps),
597
727
  density: unit2(raw.density, base.density),
598
728
  sizeJitter: unit2(raw.sizeJitter, base.sizeJitter),
599
729
  rotationJitter: unit2(raw.rotationJitter, base.rotationJitter),
@@ -601,59 +731,6 @@ function normalizeStickerSettings(raw = {}, base = DEFAULT_STICKER) {
601
731
  colorJitter: unit2(raw.colorJitter, base.colorJitter)
602
732
  };
603
733
  }
604
- function stampCoverage(kind, u, v, t) {
605
- const r = Math.hypot(u, v);
606
- const a = Math.atan2(v, u);
607
- const edge = (d) => Math.min(1, Math.max(0, d * 24 + 0.5));
608
- switch (kind) {
609
- case "dot":
610
- return edge(0.95 - r);
611
- case "ring":
612
- return edge(Math.min(0.95 - r, r - 0.6));
613
- case "star": {
614
- const f = (a + Math.PI / 2) * 5 / (2 * Math.PI) % 1;
615
- const notch = Math.min(f < 0 ? f + 1 : f, 1 - (f < 0 ? f + 1 : f)) * 2;
616
- return edge(0.95 - 0.55 * notch - r);
617
- }
618
- case "sparkle": {
619
- const q = Math.pow(Math.abs(u) / 0.95, 0.6) + Math.pow(Math.abs(v) / 0.95, 0.6);
620
- return edge((1 - q) * 0.4);
621
- }
622
- case "heart": {
623
- const x = u * 1.1;
624
- const y = -v * 1.1 + 0.15;
625
- const q = x * x + y * y - 0.55;
626
- return edge(-(q * q * q - x * x * y * y * y) * 3);
627
- }
628
- case "leaf": {
629
- const w = 0.42 * Math.sqrt(Math.max(0, 1 - u * u));
630
- const inside = edge(w - Math.abs(v + 0.15 * u * u));
631
- const rib = Math.abs(v + 0.15 * u * u) < 0.03 && Math.abs(u) < 0.85 ? 0.65 : 1;
632
- return inside * rib;
633
- }
634
- case "flower": {
635
- const petals = 0.6 + 0.35 * Math.abs(Math.cos(2.5 * a));
636
- const body = edge(petals - r);
637
- return r < 0.22 ? Math.max(body, 1) * 0.85 : body;
638
- }
639
- case "confetti": {
640
- return edge(Math.min(0.9 - Math.abs(u), 0.35 - Math.abs(v)));
641
- }
642
- case "splat": {
643
- let rr = 0.55;
644
- for (let k = 1; k <= 3; k++) rr += 0.08 * Math.sin(a * (k + 2) + _chunkPQ3H2AANcjs.hash2.call(void 0, k, 1, t) * 6.28) / k;
645
- let c = edge(rr - r);
646
- for (let k = 0; k < 4; k++) {
647
- const ang = _chunkPQ3H2AANcjs.hash2.call(void 0, k, 7, t) * Math.PI * 2;
648
- const dist = 0.72 + 0.2 * _chunkPQ3H2AANcjs.hash2.call(void 0, k, 9, t);
649
- const dx = u - Math.cos(ang) * dist;
650
- const dy = v - Math.sin(ang) * dist;
651
- c = Math.max(c, edge(0.08 + 0.06 * _chunkPQ3H2AANcjs.hash2.call(void 0, k, 11, t) - Math.hypot(dx, dy)));
652
- }
653
- return c;
654
- }
655
- }
656
- }
657
734
  function hexRgb(hex) {
658
735
  const n = parseInt(hex.slice(1, 7), 16);
659
736
  return [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
@@ -664,7 +741,22 @@ function stickerRect(s) {
664
741
  const y0 = Math.floor(s.y - r);
665
742
  return { x: x0, y: y0, width: Math.ceil(s.x + r) - x0, height: Math.ceil(s.y + r) - y0 };
666
743
  }
667
- function drawSticker(medium, s) {
744
+ function stickerStamp(s) {
745
+ if (s.ref) {
746
+ try {
747
+ return _chunkZEYMKDYScjs.editorStampOf.call(void 0, s.ref);
748
+ } catch (e4) {
749
+ return null;
750
+ }
751
+ }
752
+ return s.stamp && _chunkZEYMKDYScjs.STAMP_KINDS.includes(s.stamp) ? s.stamp : null;
753
+ }
754
+ function drawSticker(medium, s, art) {
755
+ const kind = stickerStamp(s);
756
+ if (!kind) {
757
+ const a2 = _optionalChain([art, 'optionalCall', _11 => _11(s.ref)]);
758
+ return a2 ? drawArtSticker(medium, s, a2) : stickerRect(s);
759
+ }
668
760
  const rect = stickerRect(s);
669
761
  const n = rect.width * rect.height;
670
762
  const cov = new Float32Array(n);
@@ -682,11 +774,11 @@ function drawSticker(medium, s) {
682
774
  const v = (-dx * sa + dy * ca) / rad;
683
775
  if (u < -1.05 || u > 1.05 || v < -1.05 || v > 1.05) continue;
684
776
  let c = 0;
685
- for (let k = 0; k < 4; k++) c += stampCoverage(s.stamp, u + ((k & 1) - 0.5) / rad / 2, v + ((k >> 1) - 0.5) / rad / 2, s.seed);
777
+ for (let k = 0; k < 4; k++) c += _chunkZEYMKDYScjs.stampCoverage.call(void 0, kind, u + ((k & 1) - 0.5) / rad / 2, v + ((k >> 1) - 0.5) / rad / 2, s.seed);
686
778
  c /= 4;
687
779
  if (c <= 0) continue;
688
780
  cov[p] = c;
689
- const shade = 1 + 0.12 * (-(u + v) / 2) + 0.06 * (_chunkPQ3H2AANcjs.hash2.call(void 0, px, rect.y + yy, s.seed) - 0.5);
781
+ const shade = 1 + 0.12 * (-(u + v) / 2) + 0.06 * (_chunk3K6HWEXZcjs.hash2.call(void 0, px, rect.y + yy, s.seed) - 0.5);
690
782
  field[p * 3] = Math.min(1, Math.max(0, r * shade));
691
783
  field[p * 3 + 1] = Math.min(1, Math.max(0, g * shade));
692
784
  field[p * 3 + 2] = Math.min(1, Math.max(0, b * shade));
@@ -695,11 +787,40 @@ function drawSticker(medium, s) {
695
787
  medium.deposit(rect, cov, s.opacity, [r, g, b], null, field);
696
788
  return rect;
697
789
  }
790
+ function drawArtSticker(medium, s, art) {
791
+ const rect = stickerRect(s);
792
+ const n = rect.width * rect.height;
793
+ const cov = new Float32Array(n);
794
+ const field = new Float32Array(n * 3);
795
+ const a = s.rotation * Math.PI / 180;
796
+ const ca = Math.cos(a);
797
+ const sa = Math.sin(a);
798
+ const scale = Math.max(art.width, art.height) / s.size;
799
+ const [r, g, b] = hexRgb(s.color);
800
+ const px = [0, 0, 0, 0];
801
+ for (let yy = 0, p = 0; yy < rect.height; yy++) {
802
+ for (let xx = 0; xx < rect.width; xx++, p++) {
803
+ const dx = rect.x + xx + 0.5 - s.x;
804
+ const dy = rect.y + yy + 0.5 - s.y;
805
+ const ax = (dx * ca + dy * sa) * scale + art.width / 2;
806
+ const ay = (-dx * sa + dy * ca) * scale + art.height / 2;
807
+ if (ax < -1 || ay < -1 || ax > art.width + 1 || ay > art.height + 1) continue;
808
+ sampleArt(art, ax, ay, px);
809
+ if (!(px[3] > 0)) continue;
810
+ cov[p] = px[3];
811
+ field[p * 3] = art.tintable ? r : px[0];
812
+ field[p * 3 + 1] = art.tintable ? g : px[1];
813
+ field[p * 3 + 2] = art.tintable ? b : px[2];
814
+ }
815
+ }
816
+ medium.deposit(rect, cov, s.opacity, [r, g, b], null, field);
817
+ return rect;
818
+ }
698
819
  function jitterColor(hex, amount, seed, i) {
699
820
  const [r, g, b] = hexRgb(hex);
700
821
  const lch = _chunk7AHBW2CQcjs.oklabToOklch.call(void 0, _chunk7AHBW2CQcjs.rgbToOklab.call(void 0, { r, g, b, a: 1 }));
701
- const l = Math.min(1, Math.max(0, lch.l + (_chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 61) - 0.5) * amount * 0.3));
702
- const h = lch.h + (_chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 63) - 0.5) * amount * 80;
822
+ const l = Math.min(1, Math.max(0, lch.l + (_chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 61) - 0.5) * amount * 0.3));
823
+ const h = lch.h + (_chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 63) - 0.5) * amount * 80;
703
824
  const at = (c) => _chunk7AHBW2CQcjs.oklabToRgb.call(void 0, _chunk7AHBW2CQcjs.oklchToOklab.call(void 0, { l, c, h, alpha: 1 }));
704
825
  const inGamut = (o) => [o.r, o.g, o.b].every((v) => v >= -1e-4 && v <= 1 + 1e-4);
705
826
  let out = at(lch.c);
@@ -715,8 +836,8 @@ function jitterColor(hex, amount, seed, i) {
715
836
  }
716
837
  return "#" + [out.r, out.g, out.b].map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, "0")).join("");
717
838
  }
718
- var StickerSpray = (_class4 = class {
719
- constructor(init) {;_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);_class4.prototype.__init20.call(this);_class4.prototype.__init21.call(this);
839
+ var StickerSpray = (_class5 = class {
840
+ constructor(init) {;_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);
720
841
  this.init = init;
721
842
  const b = init.brush;
722
843
  this.smoother = new PenSmoother(b.tip.smoothing, b.pressureSmoothing);
@@ -724,18 +845,20 @@ var StickerSpray = (_class4 = class {
724
845
  const spacing = Math.max(0.05, 1 / Math.max(0.05, b.sticker.density * 4));
725
846
  const walk = { ...b, tip: { ...b.tip, spacing }, dynamics: { ...b.dynamics, scatter: 0, sizeJitter: 0, angleJitter: 0 } };
726
847
  this.walkers = this.copies.map((c, i) => new PenDabWalker(walk, init.seed + i * 7919 | 0, c.linear));
727
- this.medium = new (0, _chunkPQ3H2AANcjs.TileMedium)(init.target, {});
848
+ this.medium = new (0, _chunkI7NJPHWVcjs.TileMedium)(init.target, {});
728
849
  this.next = init.nextId;
729
850
  }
730
851
 
731
- __init18() {this.dabCount = 0}
732
- __init19() {this.stickers = []}
852
+ __init20() {this.dabCount = 0}
853
+ __init21() {this.stickers = []}
854
+ /** Refs the spray picked but could not draw because their art had not loaded. */
855
+ __init22() {this.skipped = /* @__PURE__ */ new Set()}
733
856
 
734
857
 
735
858
 
736
859
 
737
- __init20() {this.lastRaw = null}
738
- __init21() {this.lastFed = null}
860
+ __init23() {this.lastRaw = null}
861
+ __init24() {this.lastFed = null}
739
862
 
740
863
  push(points) {
741
864
  const rects = [];
@@ -767,28 +890,36 @@ var StickerSpray = (_class4 = class {
767
890
  const st = this.init.brush.sticker;
768
891
  const i = this.next;
769
892
  const seed = this.init.seed;
770
- const size = d.radius * 2 * (1 - st.sizeJitter * 0.7 * _chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 51));
893
+ const size = d.radius * 2 * (1 - st.sizeJitter * 0.7 * _chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 51));
771
894
  const off = st.scatter * d.radius * 2;
772
- const a = _chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 53) * Math.PI * 2;
773
- const dist = _chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 55) * off;
895
+ const a = _chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 53) * Math.PI * 2;
896
+ const dist = _chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 55) * off;
774
897
  const base = this.colorAt(d, Math.cos(a) * dist, Math.sin(a) * dist);
775
898
  if (!(base.alpha > 0)) {
776
899
  this.next++;
777
900
  return;
778
901
  }
902
+ const ref = _nullishCoalesce(st.stamps[Math.floor(_chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 57) * st.stamps.length)], () => ( "editor:dot"));
903
+ const stamp = stickerStamp({ ref });
904
+ if (!stamp && !_optionalChain([this, 'access', _12 => _12.init, 'access', _13 => _13.art, 'optionalCall', _14 => _14(ref)])) {
905
+ this.skipped.add(ref);
906
+ this.next++;
907
+ return;
908
+ }
779
909
  const sticker = {
780
910
  id: `st${this.next++}`,
781
- stamp: _nullishCoalesce(st.stamps[Math.floor(_chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 57) * st.stamps.length)], () => ( "dot")),
911
+ ref,
912
+ ...stamp ? { stamp } : {},
782
913
  x: Math.round((d.x + Math.cos(a) * dist) * 100) / 100,
783
914
  y: Math.round((d.y + Math.sin(a) * dist) * 100) / 100,
784
915
  size: Math.max(2, Math.round(size * 10) / 10),
785
- rotation: Math.round((-d.angle * 180 / Math.PI + (_chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 59) * 2 - 1) * 180 * st.rotationJitter) * 10) / 10,
916
+ rotation: Math.round((-d.angle * 180 / Math.PI + (_chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 59) * 2 - 1) * 180 * st.rotationJitter) * 10) / 10,
786
917
  color: st.colorJitter > 0 ? jitterColor(base.color, st.colorJitter, seed, i) : base.color,
787
918
  opacity: Math.round(Math.min(1, d.opacity * this.init.brush.tip.opacity * base.alpha) * 1e3) / 1e3,
788
- seed: Math.floor(_chunkPQ3H2AANcjs.rand01.call(void 0, seed, i, 65) * 1e9)
919
+ seed: Math.floor(_chunk3K6HWEXZcjs.rand01.call(void 0, seed, i, 65) * 1e9)
789
920
  };
790
921
  this.stickers.push(sticker);
791
- rects.push(drawSticker(this.medium, sticker));
922
+ rects.push(drawSticker(this.medium, sticker, this.init.art));
792
923
  this.dabCount++;
793
924
  }
794
925
 
@@ -802,21 +933,29 @@ var StickerSpray = (_class4 = class {
802
933
  }
803
934
  const g = this.init.paint;
804
935
  if (!g) return { color: this.init.color, alpha: 1 };
805
- this.sampler ??= new (0, _chunkPQ3H2AANcjs.PaintGradientSampler)(g, this.init.seed);
936
+ this.sampler ??= new (0, _chunkI7NJPHWVcjs.PaintGradientSampler)(g, this.init.seed);
806
937
  const s = g.map.by === "across-tip" ? this.sampler.sample(((dx * Math.sin(d.direction) + dy * Math.cos(d.direction)) / Math.max(0.5, d.radius) + 1) / 2) : this.sampler.dab(d);
807
938
  return { color: "#" + s.rgb.map((c) => Math.round(Math.min(1, Math.max(0, c)) * 255).toString(16).padStart(2, "0")).join(""), alpha: s.alpha };
808
939
  }
809
- }, _class4);
810
- function renderStickers(stickers, target) {
811
- const medium = new (0, _chunkPQ3H2AANcjs.TileMedium)(target, {});
812
- for (const s of stickers) drawSticker(medium, s);
940
+ }, _class5);
941
+ function renderStickers(stickers, target, art) {
942
+ const medium = new (0, _chunkI7NJPHWVcjs.TileMedium)(target, {});
943
+ for (const s of stickers) drawSticker(medium, s, art);
813
944
  }
814
945
  function normalizeStickerUpdate(u) {
815
946
  const out = {};
947
+ if (u.ref !== void 0 && u.stamp !== void 0) throw new Error("Give ref or stamp, not both (stamp is the older name for an editor stamp).");
816
948
  if (u.stamp !== void 0) {
817
- if (!STAMP_KINDS.includes(u.stamp)) throw new Error(`stamp ${JSON.stringify(u.stamp)} is not a stamp; use ${STAMP_KINDS.join(", ")}.`);
949
+ if (!_chunkZEYMKDYScjs.STAMP_KINDS.includes(u.stamp)) throw new Error(`stamp ${JSON.stringify(u.stamp)} is not a stamp; use ${_chunkZEYMKDYScjs.STAMP_KINDS.join(", ")}, or ref for any sticker (find_stickers).`);
950
+ out.ref = `editor:${u.stamp}`;
818
951
  out.stamp = u.stamp;
819
952
  }
953
+ if (u.ref !== void 0) {
954
+ out.ref = _chunkZEYMKDYScjs.normalizeStickerRef.call(void 0, u.ref);
955
+ const stamp = stickerStamp({ ref: out.ref });
956
+ if (stamp) out.stamp = stamp;
957
+ else out.stamp = void 0;
958
+ }
820
959
  for (const k of ["x", "y"]) if (u[k] !== void 0) {
821
960
  if (!Number.isFinite(u[k])) throw new Error(`${k} must be a number of pixels.`);
822
961
  out[k] = u[k];
@@ -839,7 +978,7 @@ function normalizeStickerUpdate(u) {
839
978
  }
840
979
  return out;
841
980
  }
842
- function stickerSurface(stickers, base, dirty) {
981
+ function stickerSurface(stickers, base, dirty, art) {
843
982
  const only = base && dirty ? new Set(dirty.flatMap((r) => _chunk474IQG74cjs.tilesInRect.call(void 0, r).map(({ tx, ty }) => _chunk474IQG74cjs.tileKey.call(void 0, tx, ty)))) : null;
844
983
  const live = /* @__PURE__ */ new Map();
845
984
  let scratch = null;
@@ -857,7 +996,7 @@ function stickerSurface(stickers, base, dirty) {
857
996
  }
858
997
  };
859
998
  const reaches = (s) => !only || _chunk474IQG74cjs.tilesInRect.call(void 0, stickerRect(s)).some(({ tx, ty }) => only.has(_chunk474IQG74cjs.tileKey.call(void 0, tx, ty)));
860
- renderStickers(stickers.filter(reaches), target);
999
+ renderStickers(stickers.filter(reaches), target, art);
861
1000
  const tiles = /* @__PURE__ */ new Map();
862
1001
  if (only && base) {
863
1002
  for (const [k, t] of base.tiles) if (!only.has(k)) tiles.set(k, t);
@@ -868,7 +1007,7 @@ function stickerSurface(stickers, base, dirty) {
868
1007
  }
869
1008
  return new (0, _chunk474IQG74cjs.TiledSurface)(4, tiles, 0);
870
1009
  }
871
- function stickerAt(stickers, x, y) {
1010
+ function stickerAt(stickers, x, y, art) {
872
1011
  for (let i = stickers.length - 1; i >= 0; i--) {
873
1012
  const s = stickers[i];
874
1013
  const rad = s.size / 2;
@@ -878,21 +1017,30 @@ function stickerAt(stickers, x, y) {
878
1017
  const u = (dx * Math.cos(a) + dy * Math.sin(a)) / rad;
879
1018
  const v = (-dx * Math.sin(a) + dy * Math.cos(a)) / rad;
880
1019
  if (Math.abs(u) > 1.05 || Math.abs(v) > 1.05) continue;
881
- if (s.size < 16 || stampCoverage(s.stamp, u, v, s.seed) > 0.25) return s;
1020
+ if (s.size < 16) return s;
1021
+ const kind = stickerStamp(s);
1022
+ if (kind) {
1023
+ if (_chunkZEYMKDYScjs.stampCoverage.call(void 0, kind, u, v, s.seed) > 0.25) return s;
1024
+ continue;
1025
+ }
1026
+ const pic = _optionalChain([art, 'optionalCall', _15 => _15(s.ref)]);
1027
+ if (!pic) return s;
1028
+ const scale = Math.max(pic.width, pic.height) / 2;
1029
+ if (sampleArt(pic, u * scale + pic.width / 2, v * scale + pic.height / 2, [0, 0, 0, 0])[3] > 0.25) return s;
882
1030
  }
883
1031
  return null;
884
1032
  }
885
1033
 
886
1034
  // src/paint-editor/procedural.ts
887
1035
  var STEP_PX = 1.5;
888
- var MarkStroke = (_class5 = class {
889
- constructor(brush, options) {;_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);_class5.prototype.__init28.call(this);_class5.prototype.__init29.call(this);
1036
+ var MarkStroke = (_class6 = class {
1037
+ constructor(brush, options) {;_class6.prototype.__init25.call(this);_class6.prototype.__init26.call(this);_class6.prototype.__init27.call(this);_class6.prototype.__init28.call(this);_class6.prototype.__init29.call(this);_class6.prototype.__init30.call(this);_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);
890
1038
  this.brush = brush;
891
1039
  this.options = options;
892
- this.sampler = options.paint ? new (0, _chunkPQ3H2AANcjs.PaintGradientSampler)(options.paint, _nullishCoalesce(options.seed, () => ( 0))) : null;
893
- this.tipTable = _optionalChain([this, 'access', _7 => _7.sampler, 'optionalAccess', _8 => _8.acrossTip]) ? this.sampler.tipTable(_chunkPQ3H2AANcjs.TIP_GRADIENT_STEPS) : null;
1040
+ this.sampler = options.paint ? new (0, _chunkI7NJPHWVcjs.PaintGradientSampler)(options.paint, _nullishCoalesce(options.seed, () => ( 0))) : null;
1041
+ this.tipTable = _optionalChain([this, 'access', _16 => _16.sampler, 'optionalAccess', _17 => _17.acrossTip]) ? this.sampler.tipTable(_chunkI7NJPHWVcjs.TIP_GRADIENT_STEPS) : null;
894
1042
  this.smoother = new PenSmoother(brush.tip.smoothing, brush.pressureSmoothing);
895
- this.copies = _optionalChain([options, 'access', _9 => _9.copies, 'optionalAccess', _10 => _10.length]) ? options.copies : [IDENTITY_COPY];
1043
+ this.copies = _optionalChain([options, 'access', _18 => _18.copies, 'optionalAccess', _19 => _19.length]) ? options.copies : [IDENTITY_COPY];
896
1044
  this.seed = _nullishCoalesce(options.seed, () => ( 0));
897
1045
  const fine = { ...brush, tip: { ...brush.tip, spacing: Math.max(0.01, Math.min(0.25, STEP_PX / Math.max(1, brush.tip.size))) } };
898
1046
  this.walkers = this.copies.map((c, i) => new PenDabWalker(fine, (_nullishCoalesce(options.seed, () => ( 0))) + i * 7919 | 0, c.linear));
@@ -900,22 +1048,22 @@ var MarkStroke = (_class5 = class {
900
1048
  }
901
1049
 
902
1050
 
903
- __init22() {this.coverage = new (0, _chunkPQ3H2AANcjs.CoverageMap)()}
904
- __init23() {this.dabCount = 0}
1051
+ __init25() {this.coverage = new (0, _chunkI7NJPHWVcjs.CoverageMap)()}
1052
+ __init26() {this.dabCount = 0}
905
1053
 
906
1054
 
907
1055
 
908
1056
 
909
- __init24() {this.buf = new Float32Array(0)}
910
- __init25() {this.field = new Float32Array(0)}
911
- __init26() {this.lastRaw = null}
912
- __init27() {this.lastFed = null}
1057
+ __init27() {this.buf = new Float32Array(0)}
1058
+ __init28() {this.field = new Float32Array(0)}
1059
+ __init29() {this.lastRaw = null}
1060
+ __init30() {this.lastFed = null}
913
1061
 
914
1062
 
915
1063
 
916
1064
  /** The pen dab the marks being made belong to, and its gradient colour. */
917
- __init28() {this.pen = null}
918
- __init29() {this.penColor = { rgb: [0, 0, 0], alpha: 1 }}
1065
+ __init31() {this.pen = null}
1066
+ __init32() {this.penColor = { rgb: [0, 0, 0], alpha: 1 }}
919
1067
  push(points) {
920
1068
  const rects = [];
921
1069
  for (const raw of points) {
@@ -961,26 +1109,73 @@ var MarkStroke = (_class5 = class {
961
1109
  if (!(r > 0.05) || !(alpha > 0)) return;
962
1110
  const dab = { x, y, radius: Math.max(0.5, r), alpha: Math.min(1, alpha), opacity: Math.min(1, opacity), angle: 0, roundness: 1 };
963
1111
  const hardness = this.brush.tip.hardness;
964
- const rect = _chunkPQ3H2AANcjs.dabRect.call(void 0, dab, hardness);
1112
+ const rect = _chunkI7NJPHWVcjs.dabRect.call(void 0, dab, hardness);
965
1113
  const n = rect.width * rect.height;
966
1114
  if (n <= 0) return;
1115
+ const vals = this.scratch(n);
1116
+ _chunkI7NJPHWVcjs.rasterizeDab.call(void 0, dab, hardness, rect, vals);
1117
+ this.lay(rect, vals, dab.opacity, pressure, x, y, rects);
1118
+ }
1119
+ /**
1120
+ * Stamps a textured mark: `kind` from the sticker stamps over a square of half-size `r`, turned
1121
+ * `rotation` radians clockwise, with flow `alpha` towards `opacity`. Small marks are supersampled.
1122
+ */
1123
+ stamp(x, y, r, kind, rotation, seed, alpha, opacity, pressure, rects) {
1124
+ if (!(r > 0.05) || !(alpha > 0)) return;
1125
+ const reach = Math.max(0.5, r) * Math.SQRT2 + 1;
1126
+ const rect = { x: Math.floor(x - reach), y: Math.floor(y - reach), width: 0, height: 0 };
1127
+ rect.width = Math.ceil(x + reach) - rect.x;
1128
+ rect.height = Math.ceil(y + reach) - rect.y;
1129
+ const n = rect.width * rect.height;
1130
+ const vals = this.scratch(n);
1131
+ const ss = r < 10 ? 3 : 1;
1132
+ const c = Math.cos(rotation);
1133
+ const s = Math.sin(rotation);
1134
+ const inv = 1 / Math.max(0.5, r);
1135
+ const a = Math.min(1, alpha);
1136
+ for (let yy = 0, k = 0; yy < rect.height; yy++) {
1137
+ for (let xx = 0; xx < rect.width; xx++, k++) {
1138
+ let sum = 0;
1139
+ for (let sy = 0; sy < ss; sy++) {
1140
+ for (let sx = 0; sx < ss; sx++) {
1141
+ const dx = rect.x + xx + (sx + 0.5) / ss - x;
1142
+ const dy = rect.y + yy + (sy + 0.5) / ss - y;
1143
+ sum += _chunkZEYMKDYScjs.stampCoverage.call(void 0, kind, (dx * c + dy * s) * inv, (dy * c - dx * s) * inv, seed);
1144
+ }
1145
+ }
1146
+ vals[k] = sum / (ss * ss) * a;
1147
+ }
1148
+ }
1149
+ this.lay(rect, vals, Math.min(1, opacity), pressure, x, y, rects);
1150
+ }
1151
+ /** A scratch buffer of `n` values (reused; valid until the next mark). */
1152
+ scratch(n) {
967
1153
  if (this.buf.length < n) this.buf = new Float32Array(n);
968
1154
  const vals = this.buf.subarray(0, n);
969
- _chunkPQ3H2AANcjs.rasterizeDab.call(void 0, dab, hardness, rect, vals);
1155
+ vals.fill(0);
1156
+ return vals;
1157
+ }
1158
+ /**
1159
+ * Lays coverage `vals` over `rect` towards `opacity`: paper grain, then effect paint or the
1160
+ * colour of a mark at (x, y) (gradient paint), into the stroke's coverage.
1161
+ */
1162
+ lay(rect, vals, opacity, pressure, x, y, rects) {
1163
+ const n = rect.width * rect.height;
1164
+ if (n <= 0) return;
970
1165
  const grain = this.options.grain;
971
1166
  if (this.depth > 0 && grain) {
972
1167
  for (let yy = 0, k = 0; yy < rect.height; yy++)
973
- for (let xx = 0; xx < rect.width; xx++, k++) if (vals[k] > 0) vals[k] *= _chunkPQ3H2AANcjs.grainCatch.call(void 0, grain.at(rect.x + xx + 0.5, rect.y + yy + 0.5), this.depth, pressure);
1168
+ for (let xx = 0; xx < rect.width; xx++, k++) if (vals[k] > 0) vals[k] *= _chunkI7NJPHWVcjs.grainCatch.call(void 0, grain.at(rect.x + xx + 0.5, rect.y + yy + 0.5), this.depth, pressure);
974
1169
  }
975
1170
  if (this.options.effect) {
976
1171
  if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
977
1172
  const field = this.field.subarray(0, n * 3);
978
1173
  sampleEffectField(this.options.effect, rect, vals, field, this.pen);
979
- this.coverage.accumulate(rect, vals, dab.opacity, null, field);
1174
+ this.coverage.accumulate(rect, vals, opacity, null, field);
980
1175
  } else {
981
1176
  const color = this.markColor(x, y);
982
1177
  if (color && color.alpha < 1) for (let k = 0; k < n; k++) vals[k] *= color.alpha;
983
- this.coverage.accumulate(rect, vals, dab.opacity, color ? color.rgb : null);
1178
+ this.coverage.accumulate(rect, vals, opacity, color ? color.rgb : null);
984
1179
  }
985
1180
  rects.push(rect);
986
1181
  this.dabCount++;
@@ -992,15 +1187,42 @@ var MarkStroke = (_class5 = class {
992
1187
  const n = Math.max(1, Math.ceil(len / step));
993
1188
  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
1189
  }
995
- }, _class5);
996
- var PARTICLE_KINDS = ["string", "spring", "orbit", "flow"];
997
- var PARTICLE_RENDERS = ["dot", "line", "sequence"];
998
- var DEFAULT_PARTICLE = { kind: "spring", render: "line", count: 12, size: 0.08, spread: 0.5, stiffness: 0.4, damping: 0.25, speed: 0.5, gravity: 0, life: 0.5, interval: 0.3 };
1190
+ }, _class6);
1191
+ var PARTICLE_KINDS = ["string", "spring", "orbit", "flow", "elastic", "fuzzy"];
1192
+ var PARTICLE_RENDERS = ["dot", "line", "sequence", "ribbon"];
1193
+ var RIBBON_PAIRS = ["adjacent", "first-last"];
1194
+ var DOT_TEXTURE_ORDERS = ["sequence", "random"];
1195
+ var DOT_ROTATIONS = ["follow", "random", "fixed"];
1196
+ var MAX_DOT_TEXTURES = 4;
1197
+ var DEFAULT_PARTICLE = {
1198
+ kind: "spring",
1199
+ render: "line",
1200
+ count: 12,
1201
+ size: 0.08,
1202
+ spread: 0.5,
1203
+ stiffness: 0.4,
1204
+ damping: 0.25,
1205
+ speed: 0.5,
1206
+ gravity: 0,
1207
+ life: 0.5,
1208
+ interval: 0.3,
1209
+ ribbonFill: 0.5,
1210
+ ribbonPairs: "adjacent",
1211
+ waveAmplitude: 0,
1212
+ waveLength: 60,
1213
+ waveSpeed: 0,
1214
+ wavePhaseJitter: 0,
1215
+ dotTextures: [],
1216
+ dotTextureOrder: "sequence",
1217
+ dotRotation: "random"
1218
+ };
999
1219
  var unit3 = (v, d) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : d;
1220
+ var oneOf = (v, all, d) => all.includes(v) ? v : d;
1000
1221
  function normalizeParticle(raw = {}, base = DEFAULT_PARTICLE) {
1222
+ const textures = Array.isArray(raw.dotTextures) ? raw.dotTextures.filter((t) => _chunkZEYMKDYScjs.STAMP_KINDS.includes(t)).slice(0, MAX_DOT_TEXTURES) : [..._nullishCoalesce(base.dotTextures, () => ( []))];
1001
1223
  return {
1002
- kind: PARTICLE_KINDS.includes(raw.kind) ? raw.kind : base.kind,
1003
- render: PARTICLE_RENDERS.includes(raw.render) ? raw.render : base.render,
1224
+ kind: oneOf(raw.kind, PARTICLE_KINDS, base.kind),
1225
+ render: oneOf(raw.render, PARTICLE_RENDERS, base.render),
1004
1226
  count: typeof raw.count === "number" && Number.isFinite(raw.count) ? Math.min(64, Math.max(1, Math.round(raw.count))) : base.count,
1005
1227
  size: unit3(raw.size, base.size),
1006
1228
  spread: unit3(raw.spread, base.spread),
@@ -1009,9 +1231,61 @@ function normalizeParticle(raw = {}, base = DEFAULT_PARTICLE) {
1009
1231
  speed: unit3(raw.speed, base.speed),
1010
1232
  gravity: typeof raw.gravity === "number" && Number.isFinite(raw.gravity) ? Math.min(1, Math.max(-1, raw.gravity)) : base.gravity,
1011
1233
  life: unit3(raw.life, base.life),
1012
- interval: unit3(raw.interval, base.interval)
1234
+ interval: unit3(raw.interval, base.interval),
1235
+ ribbonFill: unit3(raw.ribbonFill, _nullishCoalesce(base.ribbonFill, () => ( DEFAULT_PARTICLE.ribbonFill))),
1236
+ ribbonPairs: oneOf(raw.ribbonPairs, RIBBON_PAIRS, _nullishCoalesce(base.ribbonPairs, () => ( DEFAULT_PARTICLE.ribbonPairs))),
1237
+ waveAmplitude: unit3(raw.waveAmplitude, _nullishCoalesce(base.waveAmplitude, () => ( 0))),
1238
+ waveLength: typeof raw.waveLength === "number" && Number.isFinite(raw.waveLength) ? Math.min(2e3, Math.max(4, raw.waveLength)) : _nullishCoalesce(base.waveLength, () => ( DEFAULT_PARTICLE.waveLength)),
1239
+ waveSpeed: unit3(raw.waveSpeed, _nullishCoalesce(base.waveSpeed, () => ( 0))),
1240
+ wavePhaseJitter: unit3(raw.wavePhaseJitter, _nullishCoalesce(base.wavePhaseJitter, () => ( 0))),
1241
+ dotTextures: textures,
1242
+ dotTextureOrder: oneOf(raw.dotTextureOrder, DOT_TEXTURE_ORDERS, _nullishCoalesce(base.dotTextureOrder, () => ( DEFAULT_PARTICLE.dotTextureOrder))),
1243
+ dotRotation: oneOf(raw.dotRotation, DOT_ROTATIONS, _nullishCoalesce(base.dotRotation, () => ( DEFAULT_PARTICLE.dotRotation)))
1013
1244
  };
1014
1245
  }
1246
+ var FUZZY_AMPLITUDE = 0.12;
1247
+ var NOMINAL_MS_PER_PX = 2;
1248
+ var RIBBON_SUB = 4;
1249
+ function chainDamp(damp, k) {
1250
+ return Math.min(damp, 1 / (1 + 0.8 * Math.sqrt(Math.max(0, k))) ** 2);
1251
+ }
1252
+ function addPolygonArea(pts, rect, out, weight = 1) {
1253
+ const n = pts.length >> 1;
1254
+ if (n < 3) return;
1255
+ let y0 = Infinity;
1256
+ let y1 = -Infinity;
1257
+ for (let i = 1; i < pts.length; i += 2) {
1258
+ if (pts[i] < y0) y0 = pts[i];
1259
+ if (pts[i] > y1) y1 = pts[i];
1260
+ }
1261
+ const r0 = Math.max(0, Math.floor(y0) - rect.y);
1262
+ const r1 = Math.min(rect.height - 1, Math.floor(y1) - rect.y);
1263
+ const xs = [];
1264
+ const w = weight / RIBBON_SUB;
1265
+ for (let row = r0; row <= r1; row++) {
1266
+ const base = row * rect.width;
1267
+ for (let sub = 0; sub < RIBBON_SUB; sub++) {
1268
+ const y = rect.y + row + (sub + 0.5) / RIBBON_SUB;
1269
+ xs.length = 0;
1270
+ for (let i = 0, j = n - 1; i < n; j = i++) {
1271
+ const yi = pts[i * 2 + 1];
1272
+ const yj = pts[j * 2 + 1];
1273
+ if (yi <= y !== yj <= y) xs.push(pts[i * 2] + (y - yi) / (yj - yi) * (pts[j * 2] - pts[i * 2]));
1274
+ }
1275
+ if (xs.length < 2) continue;
1276
+ xs.sort((a, b) => a - b);
1277
+ for (let k = 0; k + 1 < xs.length; k += 2) {
1278
+ const a = Math.max(rect.x, xs[k]);
1279
+ const b = Math.min(rect.x + rect.width, xs[k + 1]);
1280
+ if (!(b > a)) continue;
1281
+ for (let px = Math.floor(a); px < b; px++) {
1282
+ const cover = Math.min(b, px + 1) - Math.max(a, px);
1283
+ if (cover > 0) out[base + px - rect.x] += cover * w;
1284
+ }
1285
+ }
1286
+ }
1287
+ }
1288
+ }
1015
1289
  function curl(x, y, seed) {
1016
1290
  const n = (u, v) => {
1017
1291
  const x0 = Math.floor(u);
@@ -1020,7 +1294,7 @@ function curl(x, y, seed) {
1020
1294
  const fy = v - y0;
1021
1295
  const sx = fx * fx * (3 - 2 * fx);
1022
1296
  const sy = fy * fy * (3 - 2 * fy);
1023
- const h = (a2, b2) => _chunkPQ3H2AANcjs.rand01.call(void 0, seed, a2 * 73856093 + b2 * 19349663, 41);
1297
+ const h = (a2, b2) => _chunk3K6HWEXZcjs.rand01.call(void 0, seed, a2 * 73856093 + b2 * 19349663, 41);
1024
1298
  const a = h(x0, y0);
1025
1299
  const b = h(x0 + 1, y0);
1026
1300
  const c = h(x0, y0 + 1);
@@ -1030,36 +1304,85 @@ function curl(x, y, seed) {
1030
1304
  const e = 0.01;
1031
1305
  return [(n(x, y + e) - n(x, y - e)) / (2 * e), -(n(x + e, y) - n(x - e, y)) / (2 * e)];
1032
1306
  }
1033
- var ParticleStroke = (_class6 = class extends MarkStroke {
1307
+ var ParticleStroke = (_class7 = class extends MarkStroke {
1034
1308
 
1309
+ /** Ribbon pairs per copy: particles in their order around the pen (non-flow kinds). */
1035
1310
 
1036
- __init30() {this.spawn = 0}
1311
+
1312
+ __init33() {this.spawn = 0}
1313
+ /** Textured dots drawn so far, per copy (sequence order, random picks). */
1314
+
1315
+ /** Ribbon fill: the area each pixel has had so far this stroke (telescoped into the coverage). */
1316
+ __init34() {this.filled = /* @__PURE__ */ new Map()}
1317
+ __init35() {this.pts = []}
1037
1318
  constructor(brush, options) {
1038
- super(brush, options);_class6.prototype.__init30.call(this);;
1319
+ super(brush, options);_class7.prototype.__init33.call(this);_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);;
1039
1320
  this.s = brush.particle;
1040
1321
  this.sets = this.copies.map(() => []);
1322
+ this.orders = this.copies.map(() => []);
1323
+ this.dots = this.copies.map(() => 0);
1324
+ }
1325
+ /** Strand kinds: a chain of masses behind each particle. */
1326
+ get strands() {
1327
+ return this.s.kind === "string" || this.s.kind === "elastic" || this.s.kind === "fuzzy";
1328
+ }
1329
+ newParticle(x, y, ox, oy, k, phase, i, copy) {
1330
+ const s = this.s;
1331
+ const fuzzy = s.kind === "fuzzy";
1332
+ const jitter = fuzzy ? 1 : s.wavePhaseJitter;
1333
+ return {
1334
+ x,
1335
+ y,
1336
+ vx: 0,
1337
+ vy: 0,
1338
+ ox,
1339
+ oy,
1340
+ k,
1341
+ phase,
1342
+ age: 0,
1343
+ run: 0,
1344
+ bead: 0,
1345
+ alive: true,
1346
+ wavePhase: jitter > 0 ? jitter * Math.PI * 2 * _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 43 + copy) : 0,
1347
+ waveScale: fuzzy ? 0.6 + 0.8 * _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 47 + copy) : 1,
1348
+ dx: x,
1349
+ dy: y,
1350
+ px: NaN,
1351
+ py: NaN,
1352
+ fade: 1
1353
+ };
1041
1354
  }
1042
1355
  init(copy, d) {
1043
1356
  const s = this.s;
1044
1357
  const list = this.sets[copy];
1045
1358
  const size = Math.max(1, d.radius * 2);
1046
1359
  for (let i = 0; i < (s.kind === "flow" ? 0 : s.count); i++) {
1047
- const r = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 3 + copy) * s.spread * size * 0.5;
1048
- const a = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 5 + copy) * Math.PI * 2;
1360
+ const r = _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 3 + copy) * s.spread * size * 0.5;
1361
+ const a = _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 5 + copy) * Math.PI * 2;
1049
1362
  const ox = Math.cos(a) * r;
1050
1363
  const oy = Math.sin(a) * r;
1051
- const p = { x: d.x + ox, y: d.y + oy, vx: 0, vy: 0, ox, oy, k: 0.6 + 0.8 * _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 7), phase: a, age: 0, run: 0, bead: 0, alive: true };
1052
- if (s.kind === "string") p.strand = Array.from({ length: 6 }, () => ({ x: d.x + ox, y: d.y + oy, vx: 0, vy: 0 }));
1364
+ const p = this.newParticle(d.x + ox, d.y + oy, ox, oy, 0.6 + 0.8 * _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 7), a, i, copy);
1365
+ if (this.strands) p.strand = Array.from({ length: 6 }, () => ({ x: d.x + ox, y: d.y + oy, vx: 0, vy: 0 }));
1053
1366
  if (s.kind === "orbit") {
1054
1367
  p.ox = Math.max(1, r + size * 0.15);
1055
1368
  p.oy = 0;
1056
1369
  }
1057
1370
  list.push(p);
1058
1371
  }
1372
+ this.orders[copy] = list.map((_, i) => i).sort((i, j) => list[i].phase - list[j].phase || i - j);
1373
+ }
1374
+ /** The sideways wave of particle `p` at pen dab `d` (px along the normal to the pen's direction). */
1375
+ wave(p, d, size) {
1376
+ const s = this.s;
1377
+ const amp = s.kind === "fuzzy" ? Math.max(FUZZY_AMPLITUDE, s.waveAmplitude) : s.waveAmplitude;
1378
+ if (!(amp > 0)) return 0;
1379
+ const ms = _nullishCoalesce(d.time, () => ( d.travel * NOMINAL_MS_PER_PX));
1380
+ const phase = 2 * Math.PI * d.travel / (s.waveLength * p.waveScale) - s.waveSpeed * 0.025 * ms + p.wavePhase;
1381
+ return amp * size * Math.sin(phase);
1059
1382
  }
1060
1383
  advance(copy, d, rects) {
1061
1384
  const s = this.s;
1062
- const list = this.sets[copy];
1385
+ let list = this.sets[copy];
1063
1386
  if (d.index === 0 && !list.length) this.init(copy, d);
1064
1387
  const size = Math.max(1, d.radius * 2);
1065
1388
  const markR = Math.max(0.5, size * s.size / 2);
@@ -1068,22 +1391,22 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
1068
1391
  const g = s.gravity * 0.15;
1069
1392
  const op = d.opacity;
1070
1393
  const al = Math.max(0.05, d.alpha);
1394
+ const nx = Math.sin(d.direction);
1395
+ const ny = Math.cos(d.direction);
1071
1396
  if (s.kind === "flow") {
1072
1397
  this.spawn += s.count / 8;
1073
1398
  while (this.spawn >= 1) {
1074
1399
  this.spawn -= 1;
1075
1400
  const i = this.dabCount + list.length * 31 + Math.floor(d.index);
1076
- const a = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 11 + copy) * Math.PI * 2;
1077
- const r = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 13 + copy) * s.spread * size * 0.5;
1078
- list.push({ x: d.x + Math.cos(a) * r, y: d.y + Math.sin(a) * r, vx: 0, vy: 0, ox: 0, oy: 0, k: 1, phase: a, age: 0, run: 0, bead: 0, alive: true });
1401
+ const a = _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 11 + copy) * Math.PI * 2;
1402
+ const r = _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 13 + copy) * s.spread * size * 0.5;
1403
+ list.push(this.newParticle(d.x + Math.cos(a) * r, d.y + Math.sin(a) * r, 0, 0, 1, a, i, copy));
1079
1404
  }
1080
1405
  }
1081
1406
  const lifeSteps = 8 + 120 * s.life;
1082
1407
  for (let i = 0; i < list.length; i++) {
1083
1408
  const p = list[i];
1084
1409
  if (!p.alive) continue;
1085
- const px = p.x;
1086
- const py = p.y;
1087
1410
  switch (s.kind) {
1088
1411
  case "spring": {
1089
1412
  p.vx = (p.vx + (d.x + p.ox - p.x) * k * p.k) * damp;
@@ -1092,13 +1415,38 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
1092
1415
  p.y += p.vy;
1093
1416
  break;
1094
1417
  }
1095
- case "string": {
1418
+ case "string":
1419
+ case "fuzzy": {
1096
1420
  const strand = p.strand;
1421
+ const dm = s.kind === "fuzzy" ? chainDamp(damp, k * p.k) : damp;
1097
1422
  let ax = d.x + p.ox;
1098
1423
  let ay = d.y + p.oy;
1099
1424
  for (const m of strand) {
1100
- m.vx = (m.vx + (ax - m.x) * k * p.k + 0) * damp;
1101
- m.vy = (m.vy + (ay - m.y) * k * p.k + g) * damp;
1425
+ m.vx = (m.vx + (ax - m.x) * k * p.k + 0) * dm;
1426
+ m.vy = (m.vy + (ay - m.y) * k * p.k + g) * dm;
1427
+ m.x += m.vx;
1428
+ m.y += m.vy;
1429
+ ax = m.x;
1430
+ ay = m.y;
1431
+ }
1432
+ p.x = strand[strand.length - 1].x;
1433
+ p.y = strand[strand.length - 1].y;
1434
+ break;
1435
+ }
1436
+ case "elastic": {
1437
+ const strand = p.strand;
1438
+ const rest = size * 0.12;
1439
+ const ke = Math.min(0.9, (0.1 + 0.6 * s.stiffness) * p.k);
1440
+ const dm = chainDamp(damp, ke);
1441
+ let ax = d.x + p.ox;
1442
+ let ay = d.y + p.oy;
1443
+ for (const m of strand) {
1444
+ const ex = ax - m.x;
1445
+ const ey = ay - m.y;
1446
+ const len = Math.hypot(ex, ey);
1447
+ const pull = len > rest ? (len - rest) / len : 0;
1448
+ m.vx = (m.vx + ex * pull * ke) * dm;
1449
+ m.vy = (m.vy + ey * pull * ke + g) * dm;
1102
1450
  m.x += m.vx;
1103
1451
  m.y += m.vy;
1104
1452
  ax = m.x;
@@ -1109,7 +1457,7 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
1109
1457
  break;
1110
1458
  }
1111
1459
  case "orbit": {
1112
- p.phase += (0.05 + 0.3 * s.speed) * (i % 2 ? -1 : 1) * (0.7 + 0.6 * _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 17));
1460
+ p.phase += (0.05 + 0.3 * s.speed) * (i % 2 ? -1 : 1) * (0.7 + 0.6 * _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 17));
1113
1461
  p.x = d.x + Math.cos(p.phase) * p.ox;
1114
1462
  p.y = d.y + Math.sin(p.phase) * p.ox + g * p.ox;
1115
1463
  break;
@@ -1127,11 +1475,21 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
1127
1475
  break;
1128
1476
  }
1129
1477
  }
1478
+ const w = this.wave(p, d, size);
1479
+ const qx = w ? p.x + nx * w : p.x;
1480
+ const qy = w ? p.y + ny * w : p.y;
1481
+ const px = p.dx;
1482
+ const py = p.dy;
1483
+ p.px = p.dx;
1484
+ p.py = p.dy;
1485
+ p.dx = qx;
1486
+ p.dy = qy;
1130
1487
  const fade = s.kind === "flow" ? 1 - p.age / lifeSteps : 1;
1131
- const moved = Math.hypot(p.x - px, p.y - py);
1488
+ p.fade = fade;
1489
+ const moved = Math.hypot(qx - px, qy - py);
1132
1490
  if (d.index === 0 && s.kind !== "flow") continue;
1133
- if (s.render === "dot") this.mark(p.x, p.y, markR * fade, al * fade, op, d.pressure, rects);
1134
- else if (s.render === "line") this.segment(px, py, p.x, p.y, markR * fade, al * fade, op, d.pressure, rects);
1491
+ if (s.render === "dot") this.dot(copy, qx, qy, qx - px, qy - py, markR * fade, al * fade, op, d, rects);
1492
+ else if (s.render === "line" || s.render === "ribbon") this.segment(px, py, qx, qy, markR * fade, al * fade, op, d.pressure, rects);
1135
1493
  else {
1136
1494
  p.run += moved;
1137
1495
  const gap = Math.max(markR * 2, size * (0.05 + s.interval));
@@ -1139,15 +1497,103 @@ var ParticleStroke = (_class6 = class extends MarkStroke {
1139
1497
  p.run -= gap;
1140
1498
  p.bead++;
1141
1499
  const t = moved > 0 ? 1 - p.run / Math.max(moved, 1e-6) : 1;
1142
- const bx = px + (p.x - px) * Math.min(1, Math.max(0, t));
1143
- const by = py + (p.y - py) * Math.min(1, Math.max(0, t));
1144
- this.mark(bx, by, markR * (0.7 + 0.6 * (p.bead % 3 / 2)) * fade, al * fade, op, d.pressure, rects);
1500
+ const bx = px + (qx - px) * Math.min(1, Math.max(0, t));
1501
+ const by = py + (qy - py) * Math.min(1, Math.max(0, t));
1502
+ 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
1503
  }
1146
1504
  }
1147
1505
  }
1148
- if (s.kind === "flow" && list.length > 512) this.sets[copy] = list.filter((p) => p.alive);
1506
+ if (s.render === "ribbon" && !(d.index === 0 && s.kind !== "flow")) this.ribbon(copy, list, op, al, d, rects);
1507
+ if (s.kind === "flow" && list.length > 512) {
1508
+ list = list.filter((p) => p.alive);
1509
+ this.sets[copy] = list;
1510
+ }
1149
1511
  }
1150
- }, _class6);
1512
+ /** A dot (or bead): round, or a textured stamp turned by the particle's motion (mx, my), at random or to the tip angle. */
1513
+ dot(copy, x, y, mx, my, r, alpha, opacity, d, rects) {
1514
+ const s = this.s;
1515
+ const textures = s.dotTextures;
1516
+ if (!textures.length) return this.mark(x, y, r, alpha, opacity, d.pressure, rects);
1517
+ const n = this.dots[copy]++;
1518
+ const pick = s.dotTextureOrder === "random" ? Math.min(textures.length - 1, Math.floor(_chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, n, 31 + copy) * textures.length)) : n % textures.length;
1519
+ const rotation = s.dotRotation === "random" ? _chunk3K6HWEXZcjs.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;
1520
+ this.stamp(x, y, r, textures[pick], rotation, this.seed + n * 2654435761 >>> 0, alpha, opacity, d.pressure, rects);
1521
+ }
1522
+ /**
1523
+ * Ribbon fill for one step: the band between each pair of joined particles, from where they
1524
+ * were drawn last step to where they are drawn now. Bands of one step, and of consecutive steps,
1525
+ * share edges exactly, so the fill has no seams; it is telescoped into the coverage
1526
+ * (`telescope`), so it does not build up at the joins.
1527
+ */
1528
+ ribbon(copy, list, opacity, alpha, d, rects) {
1529
+ const s = this.s;
1530
+ if (!(s.ribbonFill > 0)) return;
1531
+ const live = (p) => !!p && p.alive && !Number.isNaN(p.px);
1532
+ let order;
1533
+ if (s.kind === "flow") order = list.filter(live);
1534
+ else order = this.orders[copy].map((i) => list[i]).filter(live);
1535
+ if (order.length < 2) return;
1536
+ const pairs = [];
1537
+ if (s.ribbonPairs === "first-last") pairs.push([order[0], order[order.length - 1]]);
1538
+ else for (let i = 0; i + 1 < order.length; i++) pairs.push([order[i], order[i + 1]]);
1539
+ const pts = this.pts;
1540
+ const ceil = Math.min(1, opacity * s.ribbonFill);
1541
+ for (const [a, b] of pairs) {
1542
+ pts.length = 0;
1543
+ pts.push(a.px, a.py, a.dx, a.dy, b.dx, b.dy, b.px, b.py);
1544
+ let x0 = Infinity;
1545
+ let y0 = Infinity;
1546
+ let x1 = -Infinity;
1547
+ let y1 = -Infinity;
1548
+ for (let i = 0; i < 8; i += 2) {
1549
+ x0 = Math.min(x0, pts[i]);
1550
+ x1 = Math.max(x1, pts[i]);
1551
+ y0 = Math.min(y0, pts[i + 1]);
1552
+ y1 = Math.max(y1, pts[i + 1]);
1553
+ }
1554
+ 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 };
1555
+ const area = this.scratch(rect.width * rect.height);
1556
+ addPolygonArea(pts, rect, area, Math.min(a.fade, b.fade) * alpha);
1557
+ if (this.telescope(rect, area)) this.lay(rect, area, ceil, d.pressure, (a.dx + b.dx) / 2, (a.dy + b.dy) / 2, rects);
1558
+ }
1559
+ }
1560
+ /**
1561
+ * Turns the area newly filled over `rect` into coverage to lay towards the fill's ceiling: the
1562
+ * share of what was left unfilled, so after any number of bands (in any grouping) a pixel's
1563
+ * coverage is the ceiling times the area filled there. False when nothing was added.
1564
+ */
1565
+ telescope(rect, area) {
1566
+ let any = false;
1567
+ for (let yy = 0, k = 0; yy < rect.height; yy++) {
1568
+ const gy = rect.y + yy;
1569
+ const ty = Math.floor(gy / _chunk474IQG74cjs.TILE);
1570
+ let t;
1571
+ let tx = NaN;
1572
+ for (let xx = 0; xx < rect.width; xx++, k++) {
1573
+ const add = area[k];
1574
+ if (!(add > 0)) continue;
1575
+ const gx = rect.x + xx;
1576
+ const ntx = Math.floor(gx / _chunk474IQG74cjs.TILE);
1577
+ if (ntx !== tx || !t) {
1578
+ tx = ntx;
1579
+ const key = _chunk474IQG74cjs.tileKey.call(void 0, tx, ty);
1580
+ t = this.filled.get(key);
1581
+ if (!t) {
1582
+ t = new Float32Array(_chunk474IQG74cjs.TILE * _chunk474IQG74cjs.TILE);
1583
+ this.filled.set(key, t);
1584
+ }
1585
+ }
1586
+ const ti = (gy - ty * _chunk474IQG74cjs.TILE) * _chunk474IQG74cjs.TILE + (gx - tx * _chunk474IQG74cjs.TILE);
1587
+ const before = t[ti];
1588
+ const after = Math.min(1, before + add);
1589
+ t[ti] = after;
1590
+ area[k] = before < 1 ? (after - before) / (1 - before) : 0;
1591
+ any = true;
1592
+ }
1593
+ }
1594
+ return any;
1595
+ }
1596
+ }, _class7);
1151
1597
  var DEFAULT_BRISTLE = { count: 32, stiffness: 0.5, splay: 0.5, thickness: 0.06, load: 0.6, jitter: 0.4 };
1152
1598
  function normalizeBristle(raw = {}, base = DEFAULT_BRISTLE) {
1153
1599
  return {
@@ -1168,16 +1614,16 @@ var BristleStroke = class extends MarkStroke {
1168
1614
  const s = this.s;
1169
1615
  this.tufts = this.copies.map(
1170
1616
  (_, c) => Array.from({ length: s.count }, (_2, i) => {
1171
- const a = _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 21 + c) * Math.PI * 2;
1172
- const r = Math.sqrt(_chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 23 + c));
1617
+ const a = _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 21 + c) * Math.PI * 2;
1618
+ const r = Math.sqrt(_chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 23 + c));
1173
1619
  return {
1174
1620
  u: Math.cos(a) * r,
1175
1621
  v: Math.sin(a) * r,
1176
1622
  x: 0,
1177
1623
  y: 0,
1178
- paint: 1 - s.jitter * 0.4 * _chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 25 + c),
1179
- thirst: 1 + s.jitter * (_chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 27 + c) * 2 - 1),
1180
- w: 1 + s.jitter * 0.6 * (_chunkPQ3H2AANcjs.rand01.call(void 0, this.seed, i, 29 + c) - 0.5),
1624
+ paint: 1 - s.jitter * 0.4 * _chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 25 + c),
1625
+ thirst: 1 + s.jitter * (_chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 27 + c) * 2 - 1),
1626
+ w: 1 + s.jitter * 0.6 * (_chunk3K6HWEXZcjs.rand01.call(void 0, this.seed, i, 29 + c) - 0.5),
1181
1627
  started: false
1182
1628
  };
1183
1629
  })
@@ -1382,12 +1828,12 @@ function normalizePaintBrush(raw = {}, base) {
1382
1828
  if (rb.dirty !== void 0) mixerIn.cleanEachStroke = !rb.dirty;
1383
1829
  if (rb.mixing !== void 0) mixerIn.mixing = rb.mixing;
1384
1830
  }
1385
- const mixer = _chunkPQ3H2AANcjs.normalizeMixer.call(void 0, mixerIn);
1831
+ const mixer = _chunkI7NJPHWVcjs.normalizeMixer.call(void 0, mixerIn);
1386
1832
  let behavior = normalizeBehavior(rb, b.behavior);
1387
1833
  if (engine === "mixer") behavior = { ...behavior, pickup: mixer.wet, mix: mixer.mix, dirty: !mixer.cleanEachStroke, mixing: mixer.mixing };
1388
1834
  let paint = _nullishCoalesce(b.paint, () => ( null));
1389
1835
  if (raw.paint !== void 0) {
1390
- const p = _chunkPQ3H2AANcjs.readPaintSource.call(void 0, raw.paint);
1836
+ const p = _chunkI7NJPHWVcjs.readPaintSource.call(void 0, raw.paint);
1391
1837
  paint = p && p.type === "gradient" ? p : null;
1392
1838
  } else if (rb.multicolor !== void 0) {
1393
1839
  const mc = normalizeMultiColor(rb.multicolor, _nullishCoalesce(paintToMultiColor(paint), () => ( DEFAULT_MULTI_COLOR)));
@@ -1399,7 +1845,7 @@ function normalizePaintBrush(raw = {}, base) {
1399
1845
  name: typeof raw.name === "string" && raw.name.trim() ? raw.name.trim().slice(0, 80) : b.name,
1400
1846
  category: PAINT_BRUSH_CATEGORIES.includes(raw.category) ? raw.category : b.category,
1401
1847
  engine,
1402
- tip: _chunkPQ3H2AANcjs.normalizeBrush.call(void 0, { ...b.tip, ..._nullishCoalesce(raw.tip, () => ( {})) }),
1848
+ tip: _chunkI7NJPHWVcjs.normalizeBrush.call(void 0, { ...b.tip, ..._nullishCoalesce(raw.tip, () => ( {})) }),
1403
1849
  grain: unit4(raw.grain, b.grain),
1404
1850
  pressureSmoothing: range(raw.pressureSmoothing, 0, 0.95, b.pressureSmoothing),
1405
1851
  rotation: BRUSH_ROTATIONS.includes(raw.rotation) ? raw.rotation : b.rotation,
@@ -1407,8 +1853,8 @@ function normalizePaintBrush(raw = {}, base) {
1407
1853
  mixer,
1408
1854
  loadsColor: typeof raw.loadsColor === "boolean" ? raw.loadsColor : b.loadsColor,
1409
1855
  behavior,
1410
- blender: _chunkPQ3H2AANcjs.normalizeBlender.call(void 0, _nullishCoalesce(raw.blender, () => ( {})), b.blender),
1411
- smudge: _chunkPQ3H2AANcjs.normalizeSmudge.call(void 0, _nullishCoalesce(raw.smudge, () => ( {})), b.smudge),
1856
+ blender: _chunkI7NJPHWVcjs.normalizeBlender.call(void 0, _nullishCoalesce(raw.blender, () => ( {})), b.blender),
1857
+ smudge: _chunkI7NJPHWVcjs.normalizeSmudge.call(void 0, _nullishCoalesce(raw.smudge, () => ( {})), b.smudge),
1412
1858
  wet: normalizeWetBrush(_nullishCoalesce(raw.wet, () => ( {})), _nullishCoalesce(b.wet, () => ( DEFAULT_WET_BRUSH))),
1413
1859
  impasto: normalizeImpasto(_nullishCoalesce(raw.impasto, () => ( {})), _nullishCoalesce(b.impasto, () => ( DEFAULT_IMPASTO))),
1414
1860
  particle: normalizeParticle(_nullishCoalesce(raw.particle, () => ( {})), _nullishCoalesce(b.particle, () => ( DEFAULT_PARTICLE))),
@@ -1424,16 +1870,16 @@ var BASE_BRUSH = {
1424
1870
  name: "Round",
1425
1871
  category: "custom",
1426
1872
  engine: "dab",
1427
- tip: { ..._chunkPQ3H2AANcjs.DEFAULT_BRUSH },
1873
+ tip: { ..._chunkI7NJPHWVcjs.DEFAULT_BRUSH },
1428
1874
  grain: 0,
1429
1875
  pressureSmoothing: 0,
1430
1876
  rotation: "fixed",
1431
1877
  dynamics: { ...DEFAULT_DYNAMICS },
1432
- mixer: { ..._chunkPQ3H2AANcjs.DEFAULT_MIXER },
1878
+ mixer: { ..._chunkI7NJPHWVcjs.DEFAULT_MIXER },
1433
1879
  loadsColor: true,
1434
1880
  behavior: { ...DEFAULT_BEHAVIOR, multicolor: { ...DEFAULT_MULTI_COLOR, colors: [] } },
1435
- blender: { ..._chunkPQ3H2AANcjs.DEFAULT_BLENDER },
1436
- smudge: { ..._chunkPQ3H2AANcjs.DEFAULT_SMUDGE },
1881
+ blender: { ..._chunkI7NJPHWVcjs.DEFAULT_BLENDER },
1882
+ smudge: { ..._chunkI7NJPHWVcjs.DEFAULT_SMUDGE },
1437
1883
  wet: { ...DEFAULT_WET_BRUSH },
1438
1884
  impasto: { ...DEFAULT_IMPASTO },
1439
1885
  particle: { ...DEFAULT_PARTICLE },
@@ -1897,6 +2343,66 @@ var BUILTIN_BRUSHES = [
1897
2343
  particle: { kind: "flow", render: "dot", count: 8, size: 0.06, spread: 0.7, speed: 0.9, life: 0.3 },
1898
2344
  light: { amount: 1 }
1899
2345
  }),
2346
+ builtin({
2347
+ // Silk of light: strands dragged behind the pen on slow waves, the bands between them filled.
2348
+ id: "light-ribbon",
2349
+ name: "Light ribbon",
2350
+ category: "light",
2351
+ engine: "particle",
2352
+ tip: { size: 80, hardness: 0.8, opacity: 0.9, flow: 1, smoothing: 0.35, pressureSize: false, pressureOpacity: false },
2353
+ 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 },
2354
+ light: { amount: 0.8 }
2355
+ }),
2356
+ builtin({
2357
+ // Flames: short-lived bands thrown off the pen, rising and curling in the flow.
2358
+ id: "flame-ribbon",
2359
+ name: "Flame ribbon",
2360
+ category: "light",
2361
+ engine: "particle",
2362
+ tip: { size: 50, hardness: 0.6, opacity: 0.85, flow: 1, smoothing: 0.25, pressureSize: true, pressureOpacity: false },
2363
+ 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 },
2364
+ light: { amount: 1 }
2365
+ }),
2366
+ builtin({
2367
+ // A neon tube that lags and snaps back, with a slow shimmer.
2368
+ id: "neon-strand",
2369
+ name: "Neon strand",
2370
+ category: "light",
2371
+ engine: "particle",
2372
+ tip: { size: 40, hardness: 0.9, opacity: 1, flow: 1, smoothing: 0.4, pressureSize: false, pressureOpacity: false },
2373
+ particle: { kind: "elastic", render: "line", count: 3, size: 0.05, spread: 0.25, stiffness: 0.6, damping: 0.3, waveAmplitude: 0.06, waveLength: 120 },
2374
+ light: { amount: 1 }
2375
+ }),
2376
+ builtin({
2377
+ // Smoke lit from within: thin bands thrown into a slow, rising flow.
2378
+ id: "smoke-ribbon",
2379
+ name: "Smoke ribbon",
2380
+ category: "light",
2381
+ engine: "particle",
2382
+ tip: { size: 70, hardness: 0.3, opacity: 0.45, flow: 1, smoothing: 0.3, pressureSize: true, pressureOpacity: false },
2383
+ 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 },
2384
+ light: { amount: 0.3 }
2385
+ }),
2386
+ builtin({
2387
+ // Out-of-focus lights: soft discs and rings spaced along springy paths.
2388
+ id: "bokeh-dots",
2389
+ name: "Bokeh dots",
2390
+ category: "light",
2391
+ engine: "particle",
2392
+ tip: { size: 80, hardness: 0.7, opacity: 0.6, flow: 1, smoothing: 0.3, pressureSize: true, pressureOpacity: false },
2393
+ 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" },
2394
+ light: { amount: 0.9 }
2395
+ }),
2396
+ builtin({
2397
+ // Sparks: twinkles and stars thrown into the flow, spaced along it.
2398
+ id: "twinkle-sparks",
2399
+ name: "Twinkle sparks",
2400
+ category: "light",
2401
+ engine: "particle",
2402
+ tip: { size: 50, hardness: 0.8, opacity: 1, flow: 1, smoothing: 0.1, pressureSize: true, pressureOpacity: false },
2403
+ 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" },
2404
+ light: { amount: 1 }
2405
+ }),
1900
2406
  builtin({
1901
2407
  id: "glow-wash",
1902
2408
  name: "Glow wash",
@@ -2117,4 +2623,13 @@ function parseBrushLibrary(raw, problems = []) {
2117
2623
 
2118
2624
 
2119
2625
 
2120
- 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.DEFAULT_PARTICLE = DEFAULT_PARTICLE; exports.normalizeParticle = normalizeParticle; 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;
2626
+
2627
+
2628
+
2629
+
2630
+
2631
+
2632
+
2633
+
2634
+
2635
+ 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.STICKER_ART_SIZE = STICKER_ART_SIZE; exports.defaultStickerRasterizer = defaultStickerRasterizer; exports.StickerArtCache = StickerArtCache; exports.sampleArt = sampleArt; exports.MAX_STICKER_STAMPS = MAX_STICKER_STAMPS; exports.DEFAULT_STICKER = DEFAULT_STICKER; exports.normalizeStickerSettings = normalizeStickerSettings; exports.stickerRect = stickerRect; exports.stickerStamp = stickerStamp; 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;