@pixerate/editor 0.137.0 → 0.138.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 (110) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.d.cts +3 -3
  3. package/dist/agent/index.d.ts +3 -3
  4. package/dist/agent/index.js +1 -1
  5. package/dist/animation-editor/index.cjs +57 -57
  6. package/dist/animation-editor/index.d.cts +3 -3
  7. package/dist/animation-editor/index.d.ts +3 -3
  8. package/dist/animation-editor/index.js +4 -4
  9. package/dist/{arrange-CKXK_hf7.d.cts → arrange-j_mLiFMO.d.cts} +1 -1
  10. package/dist/{arrange-BUREmgD9.d.ts → arrange-q5HQvnnB.d.ts} +1 -1
  11. package/dist/{bezier-CZ2AhjID.d.cts → bezier-CUaH8hcK.d.cts} +1 -1
  12. package/dist/{bezier-Dywv3Bw8.d.ts → bezier-DPmdGwgR.d.ts} +1 -1
  13. package/dist/{brushTypes-C7N3EBK7.d.cts → brushTypes-MX5UVs_m.d.cts} +27 -2
  14. package/dist/{brushTypes-C7N3EBK7.d.ts → brushTypes-MX5UVs_m.d.ts} +27 -2
  15. package/dist/{brushes-DJmUbIgz.d.ts → brushes-B1ABdDAs.d.ts} +2 -108
  16. package/dist/{brushes-CnvZSYYU.d.cts → brushes-DnIdJYxN.d.cts} +2 -108
  17. package/dist/{brushstrokeRender-IMFY3FF6.cjs → brushstrokeRender-PP6NTF6M.cjs} +52 -24
  18. package/dist/brushstrokeRender-PP6NTF6M.cjs.map +1 -0
  19. package/dist/{brushstrokeRender-2FAXA3XC.js → brushstrokeRender-QGEYJ4OZ.js} +40 -12
  20. package/dist/brushstrokeRender-QGEYJ4OZ.js.map +1 -0
  21. package/dist/{builtins-Cc-bH-3U.d.cts → builtins-BakDiKWf.d.cts} +3 -2
  22. package/dist/{builtins-ZU1GJnUA.d.ts → builtins-CE33vyv2.d.ts} +3 -2
  23. package/dist/canvas/index.cjs +16 -9
  24. package/dist/canvas/index.cjs.map +1 -1
  25. package/dist/canvas/index.d.cts +56 -11
  26. package/dist/canvas/index.d.ts +56 -11
  27. package/dist/canvas/index.js +10 -3
  28. package/dist/canvas/index.js.map +1 -1
  29. package/dist/{chunk-QBWEH3IM.cjs → chunk-3T5O225T.cjs} +391 -76
  30. package/dist/chunk-3T5O225T.cjs.map +1 -0
  31. package/dist/{chunk-PRSRYHQD.cjs → chunk-7MN75BXI.cjs} +36 -36
  32. package/dist/{chunk-PRSRYHQD.cjs.map → chunk-7MN75BXI.cjs.map} +1 -1
  33. package/dist/{chunk-EQQMGO42.js → chunk-C2J7GOMJ.js} +18 -5
  34. package/dist/chunk-C2J7GOMJ.js.map +1 -0
  35. package/dist/{chunk-2XS6LTUP.cjs → chunk-C46AZEBE.cjs} +104 -339
  36. package/dist/chunk-C46AZEBE.cjs.map +1 -0
  37. package/dist/{chunk-XN2NBLV5.cjs → chunk-CM3LJPR3.cjs} +82 -82
  38. package/dist/{chunk-XN2NBLV5.cjs.map → chunk-CM3LJPR3.cjs.map} +1 -1
  39. package/dist/{chunk-HUO3TI4C.js → chunk-CPSMARTX.js} +6 -241
  40. package/dist/chunk-CPSMARTX.js.map +1 -0
  41. package/dist/{chunk-KEENUAFN.cjs → chunk-DLLUOVQX.cjs} +18 -5
  42. package/dist/chunk-DLLUOVQX.cjs.map +1 -0
  43. package/dist/{chunk-LSXXAJG3.js → chunk-J7DSKTKB.js} +5 -4
  44. package/dist/chunk-J7DSKTKB.js.map +1 -0
  45. package/dist/{chunk-6S6362D2.js → chunk-OV7253FC.js} +2 -2
  46. package/dist/{chunk-5FSRTP6E.js → chunk-PQFVLOMR.js} +319 -4
  47. package/dist/chunk-PQFVLOMR.js.map +1 -0
  48. package/dist/{chunk-OIBIRXRG.js → chunk-PSPLRQL6.js} +2 -2
  49. package/dist/{chunk-SYT2TLQF.js → chunk-QBARQGSG.js} +2 -2
  50. package/dist/{chunk-5OOIUXNJ.cjs → chunk-SXOTFV3V.cjs} +6 -6
  51. package/dist/{chunk-5OOIUXNJ.cjs.map → chunk-SXOTFV3V.cjs.map} +1 -1
  52. package/dist/{chunk-DFSZO2B5.cjs → chunk-UFL5N4DK.cjs} +5 -4
  53. package/dist/chunk-UFL5N4DK.cjs.map +1 -0
  54. package/dist/{chunk-3D77LOBM.js → chunk-V45ZTW4R.js} +3 -3
  55. package/dist/{chunk-TLCTTQFF.cjs → chunk-VEHVXRJU.cjs} +17 -17
  56. package/dist/{chunk-TLCTTQFF.cjs.map → chunk-VEHVXRJU.cjs.map} +1 -1
  57. package/dist/commands/index.cjs +2 -2
  58. package/dist/commands/index.d.cts +3 -3
  59. package/dist/commands/index.d.ts +3 -3
  60. package/dist/commands/index.js +1 -1
  61. package/dist/{export-DwnZ_hvQ.d.ts → export-BwFRiItd.d.ts} +4 -4
  62. package/dist/{export-CyJdfP_Z.d.cts → export-DuN3dpCg.d.cts} +4 -4
  63. package/dist/{blending-NgF3qo3d.d.ts → grain-COgYMMHv.d.ts} +58 -1
  64. package/dist/{blending-D7jCmjO6.d.cts → grain-GCt_O2kv.d.cts} +58 -1
  65. package/dist/image-editor/index.cjs +695 -677
  66. package/dist/image-editor/index.cjs.map +1 -1
  67. package/dist/image-editor/index.d.cts +11 -11
  68. package/dist/image-editor/index.d.ts +11 -11
  69. package/dist/image-editor/index.js +23 -5
  70. package/dist/image-editor/index.js.map +1 -1
  71. package/dist/{opentype-a1KAW-qs.d.ts → opentype-T7bkbR2D.d.ts} +1 -1
  72. package/dist/{opentype-KR_jj6b_.d.cts → opentype-n99lqxEZ.d.cts} +1 -1
  73. package/dist/paint/index.cjs +21 -3
  74. package/dist/paint/index.cjs.map +1 -1
  75. package/dist/paint/index.d.cts +72 -7
  76. package/dist/paint/index.d.ts +72 -7
  77. package/dist/paint/index.js +20 -2
  78. package/dist/paint-editor/index.cjs +171 -171
  79. package/dist/paint-editor/index.cjs.map +1 -1
  80. package/dist/paint-editor/index.d.cts +75 -29
  81. package/dist/paint-editor/index.d.ts +75 -29
  82. package/dist/paint-editor/index.js +12 -12
  83. package/dist/{pathfinder-BHS4vQNm.d.cts → pathfinder-CQlldanv.d.cts} +2 -2
  84. package/dist/{pathfinder-BVvTIBMo.d.ts → pathfinder-D-V6JLSw.d.ts} +2 -2
  85. package/dist/{rand-Cw9VLrVC.d.cts → rand-D7nfeD1p.d.cts} +18 -2
  86. package/dist/{rand-8xbUb6ur.d.ts → rand-DJGxSq9d.d.ts} +18 -2
  87. package/dist/{svg-CSz3mzkw.d.ts → svg-CnDCpIy9.d.ts} +1 -1
  88. package/dist/{svg-DTb40ICU.d.cts → svg-DPJMUSNt.d.cts} +1 -1
  89. package/dist/vector/index.cjs +944 -944
  90. package/dist/vector/index.d.cts +10 -10
  91. package/dist/vector/index.d.ts +10 -10
  92. package/dist/vector/index.js +4 -4
  93. package/dist/{warp-DTOp0lON.d.cts → warp-C7rMWUgW.d.cts} +1 -1
  94. package/dist/{warp-pd7hrH4T.d.ts → warp-Doasjpyr.d.ts} +1 -1
  95. package/package.json +1 -1
  96. package/skills/pixerate-canvas/SKILL.md +12 -5
  97. package/dist/brushstrokeRender-2FAXA3XC.js.map +0 -1
  98. package/dist/brushstrokeRender-IMFY3FF6.cjs.map +0 -1
  99. package/dist/chunk-2XS6LTUP.cjs.map +0 -1
  100. package/dist/chunk-5FSRTP6E.js.map +0 -1
  101. package/dist/chunk-DFSZO2B5.cjs.map +0 -1
  102. package/dist/chunk-EQQMGO42.js.map +0 -1
  103. package/dist/chunk-HUO3TI4C.js.map +0 -1
  104. package/dist/chunk-KEENUAFN.cjs.map +0 -1
  105. package/dist/chunk-LSXXAJG3.js.map +0 -1
  106. package/dist/chunk-QBWEH3IM.cjs.map +0 -1
  107. /package/dist/{chunk-6S6362D2.js.map → chunk-OV7253FC.js.map} +0 -0
  108. /package/dist/{chunk-OIBIRXRG.js.map → chunk-PSPLRQL6.js.map} +0 -0
  109. /package/dist/{chunk-SYT2TLQF.js.map → chunk-QBARQGSG.js.map} +0 -0
  110. /package/dist/{chunk-3D77LOBM.js.map → chunk-V45ZTW4R.js.map} +0 -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; var _class7; var _class8;
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; var _class8; var _class9; var _class10;
2
2
 
3
3
 
4
4
  var _chunkSYPFUL5Bcjs = require('./chunk-SYPFUL5B.cjs');
@@ -40,6 +40,73 @@ function hash2(x, y, salt) {
40
40
  function rand01(seed, index, stream) {
41
41
  return hash2(index, stream, seed);
42
42
  }
43
+ function randSigned(seed, index, stream) {
44
+ return rand01(seed, index, stream) * 2 - 1;
45
+ }
46
+ function seedFromString(text) {
47
+ let h = 2166136261;
48
+ for (let i = 0; i < text.length; i++) {
49
+ h ^= text.charCodeAt(i);
50
+ h = Math.imul(h, 16777619);
51
+ }
52
+ return mix32(h >>> 0);
53
+ }
54
+ var smooth = (t) => t * t * (3 - 2 * t);
55
+ var Lattice = class {
56
+ constructor(px, py, salt) {
57
+ this.px = px;
58
+ this.py = py;
59
+ this.values = new Float32Array(px * py);
60
+ for (let j = 0; j < py; j++) for (let i = 0; i < px; i++) this.values[j * px + i] = hash2(i, j, salt);
61
+ }
62
+
63
+
64
+
65
+ /** Value noise at (x, y) in lattice cells; wraps every `px` / `py` cells. */
66
+ at(x, y) {
67
+ const { px, py } = this;
68
+ const x0 = Math.floor(x);
69
+ const y0 = Math.floor(y);
70
+ const tx = smooth(x - x0);
71
+ const ty = smooth(y - y0);
72
+ const i0 = (x0 % px + px) % px;
73
+ const j0 = (y0 % py + py) % py;
74
+ const i1 = (i0 + 1) % px;
75
+ const j1 = (j0 + 1) % py;
76
+ const v = this.values;
77
+ const a = v[j0 * px + i0] + (v[j0 * px + i1] - v[j0 * px + i0]) * tx;
78
+ const b = v[j1 * px + i0] + (v[j1 * px + i1] - v[j1 * px + i0]) * tx;
79
+ return a + (b - a) * ty;
80
+ }
81
+ };
82
+ var TileableNoise = (_class = class {
83
+ constructor(size, cellsX, cellsY, octaves, salt) {;_class.prototype.__init.call(this);
84
+ this.size = size;
85
+ let cx = Math.max(1, Math.round(cellsX));
86
+ let cy = Math.max(1, Math.round(cellsY));
87
+ for (let o = 0; o < octaves; o++) {
88
+ this.octaves.push(new Lattice(cx, cy, salt + o * 7919 | 0));
89
+ cx *= 2;
90
+ cy *= 2;
91
+ }
92
+ }
93
+
94
+ __init() {this.octaves = []}
95
+ /** Fractal noise at pixel (x, y). */
96
+ at(x, y) {
97
+ let sum = 0;
98
+ let amp = 1;
99
+ let norm = 0;
100
+ const u = x / this.size;
101
+ const v = y / this.size;
102
+ for (const l of this.octaves) {
103
+ sum += l.at(u * l.px, v * l.py) * amp;
104
+ norm += amp;
105
+ amp *= 0.5;
106
+ }
107
+ return norm > 0 ? sum / norm : 0.5;
108
+ }
109
+ }, _class);
43
110
 
44
111
  // src/paint/gradient-paint.ts
45
112
  var PAINT_GRADIENT_SPACES = ["pigment", "oklab", "srgb"];
@@ -587,21 +654,21 @@ function smoothPoints(points, amount) {
587
654
  return out;
588
655
  }
589
656
  var pressureOf = (p) => typeof p.pressure === "number" && Number.isFinite(p.pressure) ? Math.min(1, Math.max(0, p.pressure)) : 1;
590
- var DabWalker = (_class = class {
591
- constructor(brush) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);
657
+ var DabWalker = (_class2 = class {
658
+ constructor(brush) {;_class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);
592
659
  this.brush = brush;
593
660
  }
594
661
 
595
- __init() {this.last = null}
596
- __init2() {this.nextAt = 0}
597
- __init3() {this.pendingFirst = null}
598
- __init4() {this.index = 0}
599
- __init5() {this.travelled = 0}
600
- __init6() {this.dir = 0}
601
- __init7() {this.firstT = null}
602
- __init8() {this.lastSpeedT = null}
603
- __init9() {this.lastSpeedAt = null}
604
- __init10() {this.speed = null}
662
+ __init2() {this.last = null}
663
+ __init3() {this.nextAt = 0}
664
+ __init4() {this.pendingFirst = null}
665
+ __init5() {this.index = 0}
666
+ __init6() {this.travelled = 0}
667
+ __init7() {this.dir = 0}
668
+ __init8() {this.firstT = null}
669
+ __init9() {this.lastSpeedT = null}
670
+ __init10() {this.lastSpeedAt = null}
671
+ __init11() {this.speed = null}
605
672
  diameter(p) {
606
673
  return this.brush.pressureSize ? this.brush.size * pressureOf(p) : this.brush.size;
607
674
  }
@@ -674,7 +741,7 @@ var DabWalker = (_class = class {
674
741
  this.pendingFirst = null;
675
742
  }
676
743
  }
677
- }, _class);
744
+ }, _class2);
678
745
  function walkDabs(points, brush) {
679
746
  const w = new DabWalker(brush);
680
747
  const out = [];
@@ -732,16 +799,16 @@ function rasterizeDab(d, hardness, rect, out) {
732
799
  }
733
800
  }
734
801
  var TILE_PIXELS = _chunkO7LUD7MHcjs.TILE * _chunkO7LUD7MHcjs.TILE;
735
- var CoverageMap = (_class2 = class {constructor() { _class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this); }
736
- __init11() {this.tiles = /* @__PURE__ */ new Map()}
802
+ var CoverageMap = (_class3 = class {constructor() { _class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this); }
803
+ __init12() {this.tiles = /* @__PURE__ */ new Map()}
737
804
  /**
738
805
  * Gradient paint: the colour laid at each pixel (sRGB 0..1, 3 per pixel), when dabs carry their
739
806
  * own colours. Each dab blends its colour in by the share of coverage it adds. Missing: the
740
807
  * stroke's one colour (`StrokeApply.color`).
741
808
  */
742
- __init12() {this.colors = /* @__PURE__ */ new Map()}
743
- __init13() {this.bounds = null}
744
- __init14() {this.dirty = /* @__PURE__ */ new Set()}
809
+ __init13() {this.colors = /* @__PURE__ */ new Map()}
810
+ __init14() {this.bounds = null}
811
+ __init15() {this.dirty = /* @__PURE__ */ new Set()}
745
812
  /** The colour at a pixel (gradient paint), or `null` when the stroke has one colour there. */
746
813
  colorAt(x, y, out) {
747
814
  const tx = Math.floor(x / _chunkO7LUD7MHcjs.TILE);
@@ -829,16 +896,16 @@ var CoverageMap = (_class2 = class {constructor() { _class2.prototype.__init11.c
829
896
  this.dirty.clear();
830
897
  return out;
831
898
  }
832
- }, _class2);
899
+ }, _class3);
833
900
  function unionRect(a, b) {
834
901
  if (!a) return { ...b };
835
902
  const x = Math.min(a.x, b.x);
836
903
  const y = Math.min(a.y, b.y);
837
904
  return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
838
905
  }
839
- var StrokeCoverage = (_class3 = class {
906
+ var StrokeCoverage = (_class4 = class {
840
907
  /** `paint`: a gradient source (resolved: no `paint` / `secondary` stops) for gradient paint; `seed` for its `random` mapping. */
841
- constructor(brush, paint = null, seed = 0) {;_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);
908
+ constructor(brush, paint = null, seed = 0) {;_class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);_class4.prototype.__init20.call(this);_class4.prototype.__init21.call(this);
842
909
  this.brush = brush;
843
910
  this.walker = new DabWalker(brush);
844
911
  this.smoothing = brush.smoothing;
@@ -846,16 +913,16 @@ var StrokeCoverage = (_class3 = class {
846
913
  this.tipTable = _optionalChain([this, 'access', _3 => _3.sampler, 'optionalAccess', _4 => _4.acrossTip]) ? this.sampler.tipTable(TIP_GRADIENT_STEPS) : null;
847
914
  }
848
915
 
849
- __init15() {this.coverage = new CoverageMap()}
916
+ __init16() {this.coverage = new CoverageMap()}
850
917
 
851
918
 
852
- __init16() {this.smoothPos = null}
853
- __init17() {this.buf = new Float32Array(0)}
854
- __init18() {this.field = new Float32Array(0)}
919
+ __init17() {this.smoothPos = null}
920
+ __init18() {this.buf = new Float32Array(0)}
921
+ __init19() {this.field = new Float32Array(0)}
855
922
 
856
923
 
857
- __init19() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
858
- __init20() {this.dabCount = 0}
924
+ __init20() {this.sample = { rgb: [0, 0, 0], alpha: 1 }}
925
+ __init21() {this.dabCount = 0}
859
926
  /** Feeds input points; returns the document rectangles the new dabs touched. */
860
927
  push(points) {
861
928
  const dabs = [];
@@ -897,7 +964,7 @@ var StrokeCoverage = (_class3 = class {
897
964
  this.field = scratch.field;
898
965
  this.coverage.accumulate(rect, vals, d.opacity, color, field);
899
966
  }
900
- }, _class3);
967
+ }, _class4);
901
968
  var TIP_GRADIENT_STEPS = 32;
902
969
  function gradientDabColor(sampler, tipTable, d, params, rect, vals, scratch) {
903
970
  if (!tipTable) {
@@ -1100,7 +1167,181 @@ function coverageTileCoord(key) {
1100
1167
  return _chunkO7LUD7MHcjs.tileCoord.call(void 0, key);
1101
1168
  }
1102
1169
 
1170
+ // src/paint/grain.ts
1171
+ var GRAIN_PATTERNS = ["smooth", "cold-pressed", "hot-pressed", "rough", "vellum", "laid", "ingres", "sanded", "canvas", "linen", "gesso"];
1172
+ var GRAIN_TILE = 256;
1173
+ var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
1174
+ function patternHeights(pattern, n, seed) {
1175
+ const out = new Float32Array(n * n);
1176
+ const salt = Math.imul(seed | 0, 2654435761) + 17;
1177
+ if (pattern === "smooth") return out.fill(1);
1178
+ const noise = (cx, cy, oct, s = 0) => new TileableNoise(n, cx, cy, oct, salt + s);
1179
+ const k = n / GRAIN_TILE;
1180
+ switch (pattern) {
1181
+ case "cold-pressed": {
1182
+ const base = noise(20 * k, 20 * k, 4);
1183
+ const fine = noise(64 * k, 64 * k, 2, 3);
1184
+ for (let y = 0; y < n; y++)
1185
+ for (let x = 0; x < n; x++) out[y * n + x] = clamp01((base.at(x, y) - 0.5) * 1.8 + 0.5 + (fine.at(x, y) - 0.5) * 0.5 + (hash2(x, y, salt) - 0.5) * 0.12);
1186
+ break;
1187
+ }
1188
+ case "hot-pressed": {
1189
+ const base = noise(40 * k, 40 * k, 3);
1190
+ for (let y = 0; y < n; y++) for (let x = 0; x < n; x++) out[y * n + x] = clamp01((base.at(x, y) - 0.5) * 1.1 + 0.6 + (hash2(x, y, salt) - 0.5) * 0.08);
1191
+ break;
1192
+ }
1193
+ case "rough": {
1194
+ const base = noise(10 * k, 10 * k, 5);
1195
+ const blot = noise(28 * k, 28 * k, 2, 5);
1196
+ for (let y = 0; y < n; y++)
1197
+ for (let x = 0; x < n; x++) out[y * n + x] = clamp01((base.at(x, y) - 0.5) * 2.4 + 0.5 + (blot.at(x, y) - 0.5) * 0.7 + (hash2(x, y, salt) - 0.5) * 0.15);
1198
+ break;
1199
+ }
1200
+ case "vellum": {
1201
+ const base = noise(40 * k, 40 * k, 3);
1202
+ for (let y = 0; y < n; y++) for (let x = 0; x < n; x++) out[y * n + x] = clamp01((base.at(x, y) - 0.5) * 1.6 + 0.55 + (hash2(x, y, salt) - 0.5) * 0.2);
1203
+ break;
1204
+ }
1205
+ case "laid": {
1206
+ const pulp = noise(32 * k, 32 * k, 3);
1207
+ const chain = n / 8;
1208
+ for (let y = 0; y < n; y++) {
1209
+ const laid = 0.5 + 0.5 * Math.cos(2 * Math.PI * y / 4);
1210
+ for (let x = 0; x < n; x++) {
1211
+ const c = Math.abs((x + 0.5) % chain - chain / 2) / (chain / 2);
1212
+ const chainLine = c > 0.94 ? -0.35 : 0;
1213
+ out[y * n + x] = clamp01(0.3 + 0.5 * laid + chainLine + (pulp.at(x, y) - 0.5) * 0.6);
1214
+ }
1215
+ }
1216
+ break;
1217
+ }
1218
+ case "ingres": {
1219
+ const fibres = noise(64 * k, 12 * k, 3);
1220
+ const base = noise(16 * k, 16 * k, 3, 7);
1221
+ for (let y = 0; y < n; y++)
1222
+ for (let x = 0; x < n; x++) {
1223
+ const g = hash2(x, y, salt ^ 85);
1224
+ out[y * n + x] = clamp01((fibres.at(x, y) - 0.5) * 1.5 + (base.at(x, y) - 0.5) * 0.7 + 0.55 + (g < 0.1 ? -0.3 : 0));
1225
+ }
1226
+ break;
1227
+ }
1228
+ case "sanded": {
1229
+ const grit = noise(96 * k, 96 * k, 2);
1230
+ for (let y = 0; y < n; y++)
1231
+ for (let x = 0; x < n; x++) {
1232
+ const h = hash2(x, y, salt);
1233
+ out[y * n + x] = clamp01((grit.at(x, y) - 0.5) * 2.4 + 0.5 + (h - 0.5) * 0.7);
1234
+ }
1235
+ break;
1236
+ }
1237
+ case "canvas":
1238
+ case "linen": {
1239
+ const period = (pattern === "canvas" ? 8 : 5) * k;
1240
+ const threads = Math.max(2, 2 * Math.round(n / period / 2));
1241
+ const p = n / threads;
1242
+ const slub = noise(threads, threads, 2, 11);
1243
+ for (let y = 0; y < n; y++)
1244
+ for (let x = 0; x < n; x++) {
1245
+ const fx = (x + 0.5) / p;
1246
+ const fy = (y + 0.5) / p;
1247
+ const cx = Math.floor(fx);
1248
+ const cy = Math.floor(fy);
1249
+ const warp = Math.sin(Math.PI * (fx - cx));
1250
+ const weft = Math.sin(Math.PI * (fy - cy));
1251
+ const top = (cx + cy) % 2 === 0 ? warp : weft;
1252
+ const irregular = pattern === "linen" ? (slub.at(x, y) - 0.5) * 0.5 : 0;
1253
+ out[y * n + x] = clamp01(0.2 + 0.7 * top + irregular + (hash2(x, y, salt) - 0.5) * 0.15);
1254
+ }
1255
+ break;
1256
+ }
1257
+ case "gesso": {
1258
+ const streaks = noise(2 * k, 28 * k, 3);
1259
+ const tooth = noise(64 * k, 64 * k, 2, 13);
1260
+ for (let y = 0; y < n; y++)
1261
+ for (let x = 0; x < n; x++) out[y * n + x] = clamp01((streaks.at(x, y) - 0.5) * 1.8 + (tooth.at(x, y) - 0.5) * 0.9 + 0.55);
1262
+ break;
1263
+ }
1264
+ }
1265
+ return out;
1266
+ }
1267
+ var rawCache = /* @__PURE__ */ new Map();
1268
+ function grainHeights(pattern, seed, size = GRAIN_TILE) {
1269
+ const key = `${pattern}:${seed}:${size}`;
1270
+ let h = rawCache.get(key);
1271
+ if (!h) {
1272
+ h = patternHeights(pattern, size, seed);
1273
+ if (rawCache.size > 32) rawCache.delete(rawCache.keys().next().value);
1274
+ rawCache.set(key, h);
1275
+ }
1276
+ return h;
1277
+ }
1278
+ var PaperGrain = (_class5 = class {
1279
+ __init22() {this.size = GRAIN_TILE}
1280
+
1281
+
1282
+
1283
+
1284
+ constructor(paper, media = "dry") {;_class5.prototype.__init22.call(this);
1285
+ const raw = grainHeights(paper.pattern, paper.seed);
1286
+ const t = paper[media];
1287
+ this.flat = paper.pattern === "smooth";
1288
+ if (t.contrast === 1 && !t.invert) this.heights = raw;
1289
+ else {
1290
+ const out = new Float32Array(raw.length);
1291
+ for (let i = 0; i < raw.length; i++) {
1292
+ const v = clamp01((raw[i] - 0.5) * t.contrast + 0.5);
1293
+ out[i] = t.invert ? 1 - v : v;
1294
+ }
1295
+ this.heights = out;
1296
+ }
1297
+ this.scale = paper.scale;
1298
+ this.mirror = paper.tiling === "mirror";
1299
+ }
1300
+ /** Tile cell index for a coordinate in grain pixels (mirrored on odd tiles when mirroring). */
1301
+ wrap(v) {
1302
+ const n = this.size;
1303
+ if (!this.mirror) return (v % n + n) % n;
1304
+ const p = (v % (2 * n) + 2 * n) % (2 * n);
1305
+ return p < n ? p : 2 * n - 1 - p;
1306
+ }
1307
+ /** Height 0..1 at document position (x, y). */
1308
+ at(x, y) {
1309
+ const u = x / this.scale - 0.5;
1310
+ const v = y / this.scale - 0.5;
1311
+ const x0 = Math.floor(u);
1312
+ const y0 = Math.floor(v);
1313
+ const fx = u - x0;
1314
+ const fy = v - y0;
1315
+ const n = this.size;
1316
+ const h = this.heights;
1317
+ const a = this.wrap(x0);
1318
+ const b = this.wrap(x0 + 1);
1319
+ const c = this.wrap(y0) * n;
1320
+ const d = this.wrap(y0 + 1) * n;
1321
+ const top = h[c + a] + (h[c + b] - h[c + a]) * fx;
1322
+ const bot = h[d + a] + (h[d + b] - h[d + a]) * fx;
1323
+ return top + (bot - top) * fy;
1324
+ }
1325
+ }, _class5);
1326
+ function grainCatch(h, depth, pressure) {
1327
+ if (depth <= 0) return 1;
1328
+ const t = depth * (0.75 - 0.65 * Math.min(1, Math.max(0, pressure)));
1329
+ const u = clamp01((h - t + 0.18) / 0.36);
1330
+ return 1 - depth + depth * u * u * (3 - 2 * u);
1331
+ }
1332
+ function strokeGrainCatch(h, depth, pressure) {
1333
+ if (depth <= 0) return 1;
1334
+ const p = Math.min(1, Math.max(0, pressure));
1335
+ const amount = Math.min(1, depth * (1.4 - 0.6 * p));
1336
+ const u = clamp01((h - 0.2 - 0.25 * (1 - p)) / 0.5);
1337
+ return 1 - amount + amount * u * u * (3 - 2 * u);
1338
+ }
1339
+
1103
1340
  // src/paint/path-stroke.ts
1341
+ var JITTER_SIZE = 1;
1342
+ var JITTER_ANGLE = 2;
1343
+ var JITTER_OPACITY = 3;
1344
+ var JITTER_SCATTER = 4;
1104
1345
  function lean(s) {
1105
1346
  if (typeof s.altitude === "number" && Number.isFinite(s.altitude)) {
1106
1347
  return { amount: 1 - Math.min(1, Math.max(0, s.altitude / (Math.PI / 2))), azimuth: _nullishCoalesce(s.azimuth, () => ( 0)) };
@@ -1132,8 +1373,22 @@ function pathStrokeDabs(samples, options) {
1132
1373
  if (tilt.followAzimuth && l) angle = -l.azimuth;
1133
1374
  const roundness = Math.max(0.05, brush.roundness * (1 - 0.85 * (_nullishCoalesce(tilt.roundness, () => ( 0))) * lean0));
1134
1375
  const op = brush.pressureOpacity ? pr : 1;
1135
- const params = { travel, index: index++, pressure: pr, lean: lean0, speed, direction: dir, time: time === null || t0 === null ? null : time - t0 };
1136
- return { x, y, radius: Math.max(0.5, size / 2), alpha: brush.flow, opacity: brush.opacity * op, angle, roundness, params };
1376
+ const i = index++;
1377
+ const params = { travel, index: i, pressure: pr, lean: lean0, speed, direction: dir, time: time === null || t0 === null ? null : time - t0 };
1378
+ let diameter = size;
1379
+ let opacity = brush.opacity * op;
1380
+ const j = options.jitter;
1381
+ if (j) {
1382
+ if (j.size) diameter *= Math.max(0.05, 1 + j.size * randSigned(j.seed, i, JITTER_SIZE));
1383
+ if (j.angle) angle += j.angle * Math.PI * randSigned(j.seed, i, JITTER_ANGLE);
1384
+ if (j.opacity) opacity *= 1 - j.opacity * (randSigned(j.seed, i, JITTER_OPACITY) * 0.5 + 0.5);
1385
+ if (j.scatter) {
1386
+ const off = j.scatter * diameter * randSigned(j.seed, i, JITTER_SCATTER);
1387
+ x += Math.sin(dir) * off;
1388
+ y += Math.cos(dir) * off;
1389
+ }
1390
+ }
1391
+ return { x, y, radius: Math.max(0.5, diameter / 2), alpha: brush.flow, opacity, angle, roundness, params };
1137
1392
  };
1138
1393
  const out = [];
1139
1394
  const pressure = (s) => typeof s.pressure === "number" && Number.isFinite(s.pressure) ? s.pressure : 1;
@@ -1202,6 +1457,17 @@ function pathStrokeCoverage(samples, options) {
1202
1457
  bounds = { x, y, width: Math.max(bounds.x + bounds.width, rect.x + rect.width) - x, height: Math.max(bounds.y + bounds.height, rect.y + rect.height) - y };
1203
1458
  }
1204
1459
  }
1460
+ const grain = options.grain;
1461
+ if (grain && grain.depth > 0 && dabs.length) {
1462
+ const pressure = dabs.reduce((sum, d) => sum + (_nullishCoalesce(_optionalChain([d, 'access', _13 => _13.params, 'optionalAccess', _14 => _14.pressure]), () => ( 1))), 0) / dabs.length;
1463
+ for (const [key, t] of coverage.tiles) {
1464
+ const { tx, ty } = _chunkO7LUD7MHcjs.tileCoord.call(void 0, key);
1465
+ for (let y = 0, k = 0; y < _chunkO7LUD7MHcjs.TILE; y++) {
1466
+ const gy = ty * _chunkO7LUD7MHcjs.TILE + y + 0.5;
1467
+ for (let x = 0; x < _chunkO7LUD7MHcjs.TILE; x++, k++) if (t[k] > 0) t[k] *= strokeGrainCatch(grain.at(tx * _chunkO7LUD7MHcjs.TILE + x + 0.5, gy), grain.depth, pressure);
1468
+ }
1469
+ }
1470
+ }
1205
1471
  return { coverage, bounds, dabs: dabs.length };
1206
1472
  }
1207
1473
  function paintPathStroke(surface, samples, options) {
@@ -1209,11 +1475,11 @@ function paintPathStroke(surface, samples, options) {
1209
1475
  const brush = normalizeBrush(options.brush);
1210
1476
  const hex = (c) => "#" + [0, 1, 2].map((i) => Math.round(Math.min(255, Math.max(0, c[i]))).toString(16).padStart(2, "0")).join("");
1211
1477
  let color = options.color;
1212
- if (_optionalChain([options, 'access', _13 => _13.paint, 'optionalAccess', _14 => _14.paint, 'access', _15 => _15.type]) === "color") {
1478
+ if (_optionalChain([options, 'access', _15 => _15.paint, 'optionalAccess', _16 => _16.paint, 'access', _17 => _17.type]) === "color") {
1213
1479
  const rgb = hexToPaintRgb(options.paint.paint.color).map((v) => Math.round(v * 255));
1214
1480
  color = [rgb[0], rgb[1], rgb[2], color[3]];
1215
1481
  }
1216
- const gradient = _optionalChain([options, 'access', _16 => _16.paint, 'optionalAccess', _17 => _17.paint, 'access', _18 => _18.type]) === "gradient" ? { ...options.paint, colors: _nullishCoalesce(options.paint.colors, () => ( { paint: hex(color), secondary: "#ffffff" })) } : null;
1482
+ const gradient = _optionalChain([options, 'access', _18 => _18.paint, 'optionalAccess', _19 => _19.paint, 'access', _20 => _20.type]) === "gradient" ? { ...options.paint, colors: _nullishCoalesce(options.paint.colors, () => ( { paint: hex(color), secondary: "#ffffff" })) } : null;
1217
1483
  const { coverage, bounds, dabs } = pathStrokeCoverage(samples, { ...options, gradient });
1218
1484
  if (!bounds) return { surface, bounds: null, dabs };
1219
1485
  const offset = _nullishCoalesce(options.offset, () => ( { x: 0, y: 0 }));
@@ -1230,7 +1496,7 @@ function paintPathStroke(surface, samples, options) {
1230
1496
  const tiles = new Map(surface.tiles);
1231
1497
  for (const { tx, ty } of _chunkO7LUD7MHcjs.tilesInRect.call(void 0, { x: bounds.x - offset.x, y: bounds.y - offset.y, width: bounds.width, height: bounds.height })) {
1232
1498
  const key = _chunkO7LUD7MHcjs.tileKey.call(void 0, tx, ty);
1233
- const src = _optionalChain([surface, 'access', _19 => _19.tiles, 'access', _20 => _20.get, 'call', _21 => _21(key), 'optionalAccess', _22 => _22.data]);
1499
+ const src = _optionalChain([surface, 'access', _21 => _21.tiles, 'access', _22 => _22.get, 'call', _23 => _23(key), 'optionalAccess', _24 => _24.data]);
1234
1500
  const base = src && src.length === _chunkO7LUD7MHcjs.TILE * _chunkO7LUD7MHcjs.TILE * 4 ? src : new Uint8ClampedArray(_chunkO7LUD7MHcjs.TILE * _chunkO7LUD7MHcjs.TILE * 4);
1235
1501
  const t = _chunkO7LUD7MHcjs.finishTile.call(void 0, applyStrokeToTile(coverage, base, 4, tx, ty, apply), 4, surface.defaultValue);
1236
1502
  if (t) tiles.set(key, t);
@@ -1296,6 +1562,26 @@ function isBrushstrokeBrush(brush) {
1296
1562
  return !("tip" in brush) || !brush.tip || (_nullishCoalesce(brush.engine, () => ( "dab"))) === "dab";
1297
1563
  }
1298
1564
  var finite2 = (v) => typeof v === "number" && Number.isFinite(v);
1565
+ function brushCharacterOf(brush) {
1566
+ if (!("tip" in brush) || !brush.tip) return { grain: 0, size: 0, angle: 0, opacity: 0, scatter: 0 };
1567
+ const d = _nullishCoalesce(brush.dynamics, () => ( {}));
1568
+ const u = (v, hi = 1) => finite2(v) ? Math.min(hi, Math.max(0, v)) : 0;
1569
+ return { grain: u(brush.grain), size: u(d.sizeJitter), angle: u(d.angleJitter), opacity: u(d.opacityJitter), scatter: u(d.scatter, 4) };
1570
+ }
1571
+ var papers = /* @__PURE__ */ new Map();
1572
+ function paperGrain(paper) {
1573
+ const pattern = _nullishCoalesce(paper.pattern, () => ( "sanded"));
1574
+ const seed = finite2(paper.seed) ? Math.round(Math.abs(paper.seed)) : 1;
1575
+ const scale = finite2(paper.scale) && paper.scale > 0 ? Math.min(8, Math.max(0.25, paper.scale)) : 1;
1576
+ const key = `${pattern}:${seed}:${scale}`;
1577
+ let g = papers.get(key);
1578
+ if (!g) {
1579
+ g = new PaperGrain({ pattern, seed, scale, tiling: "mirror", dry: { contrast: 1, invert: false }, wet: { contrast: 1, invert: false } });
1580
+ if (papers.size > 16) papers.delete(papers.keys().next().value);
1581
+ papers.set(key, g);
1582
+ }
1583
+ return g;
1584
+ }
1299
1585
  function renderBrushstroke(input, brush, options = {}) {
1300
1586
  const strokeOptions = brushstrokeStrokeOptions(brush);
1301
1587
  const samples = brushstrokeSamples(input);
@@ -1306,7 +1592,7 @@ function renderBrushstroke(input, brush, options = {}) {
1306
1592
  let scale = finite2(options.scale) && options.scale > 0 ? Math.min(_chunkSYPFUL5Bcjs.BRUSHSTROKE_MAX_SCALE, Math.max(_chunkSYPFUL5Bcjs.BRUSHSTROKE_MIN_SCALE, options.scale)) : 1;
1307
1593
  if (!samples.length) return { image: { width: 0, height: 0, data: new Uint8ClampedArray(0) }, bounds: null, scale, dabs: 0 };
1308
1594
  const tip = strokeOptions.brush;
1309
- const reach = tip.size * (1 + 1.5 * (_nullishCoalesce(_optionalChain([strokeOptions, 'access', _23 => _23.tilt, 'optionalAccess', _24 => _24.size]), () => ( 0)))) + 4;
1595
+ const reach = tip.size * (1 + 1.5 * (_nullishCoalesce(_optionalChain([strokeOptions, 'access', _25 => _25.tilt, 'optionalAccess', _26 => _26.size]), () => ( 0)))) + 4;
1310
1596
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
1311
1597
  for (const s of samples) {
1312
1598
  x0 = Math.min(x0, s.x);
@@ -1318,8 +1604,27 @@ function renderBrushstroke(input, brush, options = {}) {
1318
1604
  const area = (x1 - x0 + reach) * (y1 - y0 + reach);
1319
1605
  if (area * scale * scale > maxPixels) scale = Math.max(_chunkSYPFUL5Bcjs.BRUSHSTROKE_MIN_SCALE, Math.sqrt(maxPixels / area));
1320
1606
  const scaled = samples.map((s) => ({ ...s, x: s.x * scale, y: s.y * scale }));
1607
+ let character = {};
1608
+ if (options.character) {
1609
+ const c = brushCharacterOf(brush);
1610
+ const ch = options.character;
1611
+ const seed = finite2(ch.seed) ? ch.seed | 0 : hashString(JSON.stringify(input));
1612
+ const jitter = c.size || c.angle || c.opacity || c.scatter ? { seed, size: c.size, angle: c.angle, opacity: c.opacity, scatter: c.scatter } : void 0;
1613
+ const paper = ch.paper === null ? null : _nullishCoalesce(ch.paper, () => ( {}));
1614
+ const depth = paper ? Math.min(1, Math.max(0, (finite2(paper.depth) ? paper.depth : 0.5) * c.grain)) : 0;
1615
+ let grain;
1616
+ if (paper && depth > 0) {
1617
+ const g = paperGrain(paper);
1618
+ const ox = _nullishCoalesce(_optionalChain([ch, 'access', _27 => _27.origin, 'optionalAccess', _28 => _28.x]), () => ( 0));
1619
+ const oy = _nullishCoalesce(_optionalChain([ch, 'access', _29 => _29.origin, 'optionalAccess', _30 => _30.y]), () => ( 0));
1620
+ const k = (finite2(ch.unit) && ch.unit > 0 ? ch.unit : 1) / scale;
1621
+ grain = { depth, at: (x, y) => g.at(ox + x * k, oy + y * k) };
1622
+ }
1623
+ character = { ...jitter ? { jitter } : {}, ...grain ? { grain } : {} };
1624
+ }
1321
1625
  const { surface, bounds, dabs } = paintPathStroke(_chunkO7LUD7MHcjs.emptySurface.call(void 0, 4), scaled, {
1322
1626
  ...strokeOptions,
1627
+ ...character,
1323
1628
  brush: { ...tip, size: tip.size * scale },
1324
1629
  closed: _nullishCoalesce(options.closed, () => ( strokeOptions.closed)),
1325
1630
  color,
@@ -1361,18 +1666,19 @@ function hashString(s, h = 2166136261) {
1361
1666
  function brushstrokeKey(input, brush, options = {}) {
1362
1667
  const path = JSON.stringify(input);
1363
1668
  const { brush: tip, ...rest } = brushstrokeStrokeOptions(brush);
1364
- const rest2 = JSON.stringify([tip, rest, _nullishCoalesce(_nullishCoalesce(options.paint, () => ( options.color)), () => ( null)), _nullishCoalesce(options.seed, () => ( 0)), _nullishCoalesce(options.closed, () => ( false)), _nullishCoalesce(options.maxPixels, () => ( null))]);
1669
+ const character = options.character ? [options.character, brushCharacterOf(brush)] : null;
1670
+ const rest2 = JSON.stringify([tip, rest, _nullishCoalesce(_nullishCoalesce(options.paint, () => ( options.color)), () => ( null)), _nullishCoalesce(options.seed, () => ( 0)), _nullishCoalesce(options.closed, () => ( false)), _nullishCoalesce(options.maxPixels, () => ( null)), ...character ? [character] : []]);
1365
1671
  const scale = finite2(options.scale) ? options.scale : 1;
1366
1672
  return `${hashString(path).toString(16)}:${path.length}:${hashString(rest2).toString(16)}:${scale}`;
1367
1673
  }
1368
- var BrushstrokeCache = (_class4 = class {
1369
- constructor(capacity = 64) {;_class4.prototype.__init21.call(this);_class4.prototype.__init22.call(this);
1674
+ var BrushstrokeCache = (_class6 = class {
1675
+ constructor(capacity = 64) {;_class6.prototype.__init23.call(this);_class6.prototype.__init24.call(this);
1370
1676
  this.capacity = capacity;
1371
1677
  }
1372
1678
 
1373
- __init21() {this.entries = /* @__PURE__ */ new Map()}
1679
+ __init23() {this.entries = /* @__PURE__ */ new Map()}
1374
1680
  /** Renders that missed the cache (for tests and stats). */
1375
- __init22() {this.misses = 0}
1681
+ __init24() {this.misses = 0}
1376
1682
  get size() {
1377
1683
  return this.entries.size;
1378
1684
  }
@@ -1394,7 +1700,7 @@ var BrushstrokeCache = (_class4 = class {
1394
1700
  clear() {
1395
1701
  this.entries.clear();
1396
1702
  }
1397
- }, _class4);
1703
+ }, _class6);
1398
1704
 
1399
1705
  // src/paint/exchange.ts
1400
1706
  var MIXING_MODELS = ["rgb", "pigment"];
@@ -1404,8 +1710,8 @@ var MIXER_DRAIN = 0.08;
1404
1710
  function exchangeRates(pickup, mix, load, mixing) {
1405
1711
  return { pickup: MIXER_PICKUP_RATE * pickup, mix: mix * pickup, drain: MIXER_DRAIN * (1 - load), mixing };
1406
1712
  }
1407
- var PaintExchange = (_class5 = class {
1408
- constructor(rates, load) {;_class5.prototype.__init23.call(this);
1713
+ var PaintExchange = (_class7 = class {
1714
+ constructor(rates, load) {;_class7.prototype.__init25.call(this);
1409
1715
  this.rates = rates;
1410
1716
  this.reservoir = load.reservoir ? [load.reservoir[0], load.reservoir[1], load.reservoir[2]] : null;
1411
1717
  this.amount = this.reservoir ? Math.min(1, Math.max(0, load.amount)) : 0;
@@ -1417,7 +1723,7 @@ var PaintExchange = (_class5 = class {
1417
1723
 
1418
1724
 
1419
1725
 
1420
- __init23() {this.out = [0, 0, 0]}
1726
+ __init25() {this.out = [0, 0, 0]}
1421
1727
  /** What the brush carries now. */
1422
1728
  get load() {
1423
1729
  return { reservoir: this.reservoir ? [...this.reservoir] : null, amount: this.amount, pickup: this.well ? [...this.well] : null };
@@ -1428,7 +1734,7 @@ var PaintExchange = (_class5 = class {
1428
1734
  }
1429
1735
  /** The prepared pigment of the last `paint` colour (pigment mixing only), for `deposit`. */
1430
1736
  get paintPigment() {
1431
- return _nullishCoalesce(_optionalChain([this, 'access', _25 => _25.pig, 'optionalAccess', _26 => _26.paint]), () => ( null));
1737
+ return _nullishCoalesce(_optionalChain([this, 'access', _31 => _31.pig, 'optionalAccess', _32 => _32.paint]), () => ( null));
1432
1738
  }
1433
1739
  /** Recolours the reservoir without refilling it (a multi-coloured brush, per dab). */
1434
1740
  setReservoirColor(r, g, b) {
@@ -1465,7 +1771,7 @@ var PaintExchange = (_class5 = class {
1465
1771
  if (!this.well) this.well = [sample.rgb[0], sample.rgb[1], sample.rgb[2]];
1466
1772
  else {
1467
1773
  if (pig) _chunkO7LUD7MHcjs.setPigmentColor.call(void 0, pig.canvas, sample.rgb[0], sample.rgb[1], sample.rgb[2]);
1468
- const o = this.mix(this.well, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _27 => _27.well]), () => ( null)), sample.rgb, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _28 => _28.canvas]), () => ( null)), this.rates.pickup * sample.contact);
1774
+ const o = this.mix(this.well, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _33 => _33.well]), () => ( null)), sample.rgb, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _34 => _34.canvas]), () => ( null)), this.rates.pickup * sample.contact);
1469
1775
  this.well = [o[0], o[1], o[2]];
1470
1776
  }
1471
1777
  if (pig) _chunkO7LUD7MHcjs.setPigmentColor.call(void 0, pig.well, this.well[0], this.well[1], this.well[2]);
@@ -1483,7 +1789,7 @@ var PaintExchange = (_class5 = class {
1483
1789
  if (!(total > 1e-6)) return 0;
1484
1790
  const pig = this.pig;
1485
1791
  if (res && this.well) {
1486
- const m = this.mix(res, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _29 => _29.res]), () => ( null)), this.well, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _30 => _30.well]), () => ( null)), wp / total);
1792
+ const m = this.mix(res, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _35 => _35.res]), () => ( null)), this.well, _nullishCoalesce(_optionalChain([pig, 'optionalAccess', _36 => _36.well]), () => ( null)), wp / total);
1487
1793
  out[0] = m[0];
1488
1794
  out[1] = m[1];
1489
1795
  out[2] = m[2];
@@ -1500,29 +1806,29 @@ var PaintExchange = (_class5 = class {
1500
1806
  drain() {
1501
1807
  this.amount *= 1 - this.rates.drain;
1502
1808
  }
1503
- }, _class5);
1809
+ }, _class7);
1504
1810
  function put255(px, i, v) {
1505
1811
  if (px instanceof Float32Array) px[i] = v > 0 ? v * 255 : 0;
1506
1812
  else px[i] = Math.round(v * 255);
1507
1813
  }
1508
- var TileMedium = (_class6 = class {
1509
- constructor(target, options = {}) {;_class6.prototype.__init24.call(this);_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);_class6.prototype.__init33.call(this);
1814
+ var TileMedium = (_class8 = class {
1815
+ constructor(target, options = {}) {;_class8.prototype.__init26.call(this);_class8.prototype.__init27.call(this);_class8.prototype.__init28.call(this);_class8.prototype.__init29.call(this);_class8.prototype.__init30.call(this);_class8.prototype.__init31.call(this);_class8.prototype.__init32.call(this);_class8.prototype.__init33.call(this);_class8.prototype.__init34.call(this);_class8.prototype.__init35.call(this);
1510
1816
  this.target = target;
1511
1817
  this.options = options;
1512
1818
  }
1513
1819
 
1514
1820
 
1515
1821
  // The tile last looked up by `locate` (and whether it came from `writeTile`).
1516
- __init24() {this.tx = NaN}
1517
- __init25() {this.ty = NaN}
1518
- __init26() {this.tile = null}
1519
- __init27() {this.writable = false}
1520
- __init28() {this.canvasPig = _chunkO7LUD7MHcjs.createPigment.call(void 0, [0, 0, 0])}
1521
- __init29() {this.paintPig = _chunkO7LUD7MHcjs.createPigment.call(void 0, [0, 0, 0])}
1522
- __init30() {this.paintKey = -1}
1523
- __init31() {this.cb = [0, 0, 0, 0]}
1524
- __init32() {this.src = [0, 0, 0, 1]}
1525
- __init33() {this.o = [0, 0, 0]}
1822
+ __init26() {this.tx = NaN}
1823
+ __init27() {this.ty = NaN}
1824
+ __init28() {this.tile = null}
1825
+ __init29() {this.writable = false}
1826
+ __init30() {this.canvasPig = _chunkO7LUD7MHcjs.createPigment.call(void 0, [0, 0, 0])}
1827
+ __init31() {this.paintPig = _chunkO7LUD7MHcjs.createPigment.call(void 0, [0, 0, 0])}
1828
+ __init32() {this.paintKey = -1}
1829
+ __init33() {this.cb = [0, 0, 0, 0]}
1830
+ __init34() {this.src = [0, 0, 0, 1]}
1831
+ __init35() {this.o = [0, 0, 0]}
1526
1832
  /** Forgets the cached tile (call when another writer may have replaced tiles). */
1527
1833
  reset() {
1528
1834
  this.tx = NaN;
@@ -1667,7 +1973,7 @@ var TileMedium = (_class6 = class {
1667
1973
  }
1668
1974
  }
1669
1975
  }
1670
- }, _class6);
1976
+ }, _class8);
1671
1977
 
1672
1978
  // src/paint/blending.ts
1673
1979
  var DEFAULT_BLENDER = { strength: 0.5, distance: 0.35, mixing: "pigment" };
@@ -1783,8 +2089,8 @@ function blenderDab(medium, rect, cov, opacity, radius, settings, scratch) {
1783
2089
  }
1784
2090
  }
1785
2091
  }
1786
- var SmudgeTip = (_class7 = class {
1787
- constructor(settings, diameter) {;_class7.prototype.__init34.call(this);_class7.prototype.__init35.call(this);
2092
+ var SmudgeTip = (_class9 = class {
2093
+ constructor(settings, diameter) {;_class9.prototype.__init36.call(this);_class9.prototype.__init37.call(this);
1788
2094
  this.settings = settings;
1789
2095
  this.size = Math.max(2, Math.min(512, Math.ceil(diameter)));
1790
2096
  this.patch = new Float32Array(this.size * this.size * 4);
@@ -1792,8 +2098,8 @@ var SmudgeTip = (_class7 = class {
1792
2098
 
1793
2099
 
1794
2100
 
1795
- __init34() {this.loaded = false}
1796
- __init35() {this.out = [0, 0, 0]}
2101
+ __init36() {this.loaded = false}
2102
+ __init37() {this.out = [0, 0, 0]}
1797
2103
  cell(d, px, py) {
1798
2104
  const s = this.size;
1799
2105
  const r = Math.max(0.5, d.radius);
@@ -1904,7 +2210,7 @@ var SmudgeTip = (_class7 = class {
1904
2210
  }
1905
2211
  }
1906
2212
  }
1907
- }, _class7);
2213
+ }, _class9);
1908
2214
 
1909
2215
  // src/paint/mixer.ts
1910
2216
  var DEFAULT_MIXER = {
@@ -1937,22 +2243,22 @@ function loadMixer(load, color) {
1937
2243
  function cleanMixer(load) {
1938
2244
  return { reservoir: load.reservoir, amount: load.amount, pickup: null };
1939
2245
  }
1940
- var MixerStroke = (_class8 = class {
1941
- constructor(brush, settings, load, target) {;_class8.prototype.__init36.call(this);_class8.prototype.__init37.call(this);_class8.prototype.__init38.call(this);_class8.prototype.__init39.call(this);_class8.prototype.__init40.call(this);
2246
+ var MixerStroke = (_class10 = class {
2247
+ constructor(brush, settings, load, target) {;_class10.prototype.__init38.call(this);_class10.prototype.__init39.call(this);_class10.prototype.__init40.call(this);_class10.prototype.__init41.call(this);_class10.prototype.__init42.call(this);
1942
2248
  this.brush = brush;
1943
2249
  this.walker = new DabWalker(brush);
1944
2250
  this.exchange = new PaintExchange(mixerRates(normalizeMixer(settings)), load);
1945
2251
  this.medium = new TileMedium(target);
1946
2252
  }
1947
2253
 
1948
- __init36() {this.dabCount = 0}
1949
- __init37() {this.bounds = null}
2254
+ __init38() {this.dabCount = 0}
2255
+ __init39() {this.bounds = null}
1950
2256
 
1951
- __init38() {this.smoothPos = null}
1952
- __init39() {this.buf = new Float32Array(0)}
2257
+ __init40() {this.smoothPos = null}
2258
+ __init41() {this.buf = new Float32Array(0)}
1953
2259
 
1954
2260
 
1955
- __init40() {this.paintColor = [0, 0, 0]}
2261
+ __init42() {this.paintColor = [0, 0, 0]}
1956
2262
  /** The brush load after the dabs so far. */
1957
2263
  get load() {
1958
2264
  return this.exchange.load;
@@ -2000,7 +2306,7 @@ var MixerStroke = (_class8 = class {
2000
2306
  }
2001
2307
  ex.drain();
2002
2308
  }
2003
- }, _class8);
2309
+ }, _class10);
2004
2310
  function union(a, b) {
2005
2311
  if (!a) return { ...b };
2006
2312
  const x = Math.min(a.x, b.x);
@@ -2095,5 +2401,14 @@ function union(a, b) {
2095
2401
 
2096
2402
 
2097
2403
 
2098
- exports.mix32 = mix32; exports.hash2 = hash2; exports.rand01 = rand01; exports.PAINT_GRADIENT_SPACES = PAINT_GRADIENT_SPACES; exports.PAINT_GRADIENT_REPEATS = PAINT_GRADIENT_REPEATS; exports.PAINT_GRADIENT_MAPPINGS = PAINT_GRADIENT_MAPPINGS; exports.DEFAULT_PAINT_GRADIENT_MAPPING = DEFAULT_PAINT_GRADIENT_MAPPING; exports.PAINT_GRADIENT_FULL_SPEED = PAINT_GRADIENT_FULL_SPEED; exports.PaintSourceError = PaintSourceError; exports.normalizeHexColor = normalizeHexColor; exports.hexToPaintRgb = hexToPaintRgb; exports.parsePaintSource = parsePaintSource; exports.readPaintSource = readPaintSource; exports.resolvePaintGradient = resolvePaintGradient; exports.scalePaintGradient = scalePaintGradient; exports.solidPaint = solidPaint; exports.isGradientPaint = isGradientPaint; exports.paintSourceColor = paintSourceColor; exports.paintSourceCss = paintSourceCss; exports.PaintGradientSampler = PaintGradientSampler; exports.PAINT_GRADIENT_JSON_SCHEMA = PAINT_GRADIENT_JSON_SCHEMA; exports.PAINT_COLOR_JSON_SCHEMA = PAINT_COLOR_JSON_SCHEMA; exports.PAINT_SOURCE_JSON_SCHEMA = PAINT_SOURCE_JSON_SCHEMA; exports.PAINT_GRADIENT_MAPPING_LABELS = PAINT_GRADIENT_MAPPING_LABELS; exports.PAINT_GRADIENT_SPACE_LABELS = PAINT_GRADIENT_SPACE_LABELS; exports.PAINT_GRADIENT_REPEAT_LABELS = PAINT_GRADIENT_REPEAT_LABELS; exports.DEFAULT_PAINT_GRADIENT = DEFAULT_PAINT_GRADIENT; exports.PAINT_GRADIENT_PRESETS = PAINT_GRADIENT_PRESETS; exports.addPaintGradientStop = addPaintGradientStop; exports.removePaintGradientStop = removePaintGradientStop; exports.updatePaintGradientStop = updatePaintGradientStop; exports.reversePaintGradient = reversePaintGradient; exports.paintGradientMappingFor = paintGradientMappingFor; exports.PAINT_GRADIENT_KIT = PAINT_GRADIENT_KIT; exports.DEFAULT_BRUSH = DEFAULT_BRUSH; exports.MAX_BRUSH_SIZE = MAX_BRUSH_SIZE; exports.normalizeBrush = normalizeBrush; exports.tipShape = tipShape; exports.tipFalloff = tipFalloff; exports.tipReach = tipReach; exports.dabCoverage = dabCoverage; exports.smoothPoints = smoothPoints; exports.DabWalker = DabWalker; exports.walkDabs = walkDabs; exports.dabRect = dabRect; exports.rasterizeDab = rasterizeDab; exports.CoverageMap = CoverageMap; exports.StrokeCoverage = StrokeCoverage; exports.TIP_GRADIENT_STEPS = TIP_GRADIENT_STEPS; exports.gradientDabColor = gradientDabColor; exports.applyStrokeToTile = applyStrokeToTile; exports.applyStrokeToDeepTile = applyStrokeToDeepTile; exports.coverageTileCoord = coverageTileCoord; exports.pathStrokeDabs = pathStrokeDabs; exports.pathStrokeCoverage = pathStrokeCoverage; exports.paintPathStroke = paintPathStroke; exports.brushstrokeSamples = brushstrokeSamples; exports.brushstrokeStrokeOptions = brushstrokeStrokeOptions; exports.isBrushstrokeBrush = isBrushstrokeBrush; exports.renderBrushstroke = renderBrushstroke; exports.brushstrokeDataUrl = brushstrokeDataUrl; exports.brushstrokeToCanvas = brushstrokeToCanvas; exports.brushstrokeKey = brushstrokeKey; exports.BrushstrokeCache = BrushstrokeCache; exports.MIXING_MODELS = MIXING_MODELS; exports.EMPTY_MIXER_LOAD = EMPTY_MIXER_LOAD; exports.MIXER_PICKUP_RATE = MIXER_PICKUP_RATE; exports.MIXER_DRAIN = MIXER_DRAIN; exports.exchangeRates = exchangeRates; exports.PaintExchange = PaintExchange; exports.put255 = put255; exports.TileMedium = TileMedium; exports.DEFAULT_BLENDER = DEFAULT_BLENDER; exports.DEFAULT_SMUDGE = DEFAULT_SMUDGE; exports.normalizeBlender = normalizeBlender; exports.normalizeSmudge = normalizeSmudge; exports.blendRadius = blendRadius; exports.createBlenderScratch = createBlenderScratch; exports.blenderDab = blenderDab; exports.SmudgeTip = SmudgeTip; exports.DEFAULT_MIXER = DEFAULT_MIXER; exports.normalizeMixer = normalizeMixer; exports.mixerRates = mixerRates; exports.loadMixer = loadMixer; exports.cleanMixer = cleanMixer; exports.MixerStroke = MixerStroke;
2099
- //# sourceMappingURL=chunk-QBWEH3IM.cjs.map
2404
+
2405
+
2406
+
2407
+
2408
+
2409
+
2410
+
2411
+
2412
+
2413
+ exports.mix32 = mix32; exports.hash2 = hash2; exports.rand01 = rand01; exports.randSigned = randSigned; exports.seedFromString = seedFromString; exports.TileableNoise = TileableNoise; exports.PAINT_GRADIENT_SPACES = PAINT_GRADIENT_SPACES; exports.PAINT_GRADIENT_REPEATS = PAINT_GRADIENT_REPEATS; exports.PAINT_GRADIENT_MAPPINGS = PAINT_GRADIENT_MAPPINGS; exports.DEFAULT_PAINT_GRADIENT_MAPPING = DEFAULT_PAINT_GRADIENT_MAPPING; exports.PAINT_GRADIENT_FULL_SPEED = PAINT_GRADIENT_FULL_SPEED; exports.PaintSourceError = PaintSourceError; exports.normalizeHexColor = normalizeHexColor; exports.hexToPaintRgb = hexToPaintRgb; exports.parsePaintSource = parsePaintSource; exports.readPaintSource = readPaintSource; exports.resolvePaintGradient = resolvePaintGradient; exports.scalePaintGradient = scalePaintGradient; exports.solidPaint = solidPaint; exports.isGradientPaint = isGradientPaint; exports.paintSourceColor = paintSourceColor; exports.paintSourceCss = paintSourceCss; exports.PaintGradientSampler = PaintGradientSampler; exports.PAINT_GRADIENT_JSON_SCHEMA = PAINT_GRADIENT_JSON_SCHEMA; exports.PAINT_COLOR_JSON_SCHEMA = PAINT_COLOR_JSON_SCHEMA; exports.PAINT_SOURCE_JSON_SCHEMA = PAINT_SOURCE_JSON_SCHEMA; exports.PAINT_GRADIENT_MAPPING_LABELS = PAINT_GRADIENT_MAPPING_LABELS; exports.PAINT_GRADIENT_SPACE_LABELS = PAINT_GRADIENT_SPACE_LABELS; exports.PAINT_GRADIENT_REPEAT_LABELS = PAINT_GRADIENT_REPEAT_LABELS; exports.DEFAULT_PAINT_GRADIENT = DEFAULT_PAINT_GRADIENT; exports.PAINT_GRADIENT_PRESETS = PAINT_GRADIENT_PRESETS; exports.addPaintGradientStop = addPaintGradientStop; exports.removePaintGradientStop = removePaintGradientStop; exports.updatePaintGradientStop = updatePaintGradientStop; exports.reversePaintGradient = reversePaintGradient; exports.paintGradientMappingFor = paintGradientMappingFor; exports.PAINT_GRADIENT_KIT = PAINT_GRADIENT_KIT; exports.DEFAULT_BRUSH = DEFAULT_BRUSH; exports.MAX_BRUSH_SIZE = MAX_BRUSH_SIZE; exports.normalizeBrush = normalizeBrush; exports.tipShape = tipShape; exports.tipFalloff = tipFalloff; exports.tipReach = tipReach; exports.dabCoverage = dabCoverage; exports.smoothPoints = smoothPoints; exports.DabWalker = DabWalker; exports.walkDabs = walkDabs; exports.dabRect = dabRect; exports.rasterizeDab = rasterizeDab; exports.CoverageMap = CoverageMap; exports.StrokeCoverage = StrokeCoverage; exports.TIP_GRADIENT_STEPS = TIP_GRADIENT_STEPS; exports.gradientDabColor = gradientDabColor; exports.applyStrokeToTile = applyStrokeToTile; exports.applyStrokeToDeepTile = applyStrokeToDeepTile; exports.coverageTileCoord = coverageTileCoord; exports.GRAIN_PATTERNS = GRAIN_PATTERNS; exports.GRAIN_TILE = GRAIN_TILE; exports.grainHeights = grainHeights; exports.PaperGrain = PaperGrain; exports.grainCatch = grainCatch; exports.strokeGrainCatch = strokeGrainCatch; exports.pathStrokeDabs = pathStrokeDabs; exports.pathStrokeCoverage = pathStrokeCoverage; exports.paintPathStroke = paintPathStroke; exports.brushstrokeSamples = brushstrokeSamples; exports.brushstrokeStrokeOptions = brushstrokeStrokeOptions; exports.isBrushstrokeBrush = isBrushstrokeBrush; exports.renderBrushstroke = renderBrushstroke; exports.brushstrokeDataUrl = brushstrokeDataUrl; exports.brushstrokeToCanvas = brushstrokeToCanvas; exports.brushstrokeKey = brushstrokeKey; exports.BrushstrokeCache = BrushstrokeCache; exports.MIXING_MODELS = MIXING_MODELS; exports.EMPTY_MIXER_LOAD = EMPTY_MIXER_LOAD; exports.MIXER_PICKUP_RATE = MIXER_PICKUP_RATE; exports.MIXER_DRAIN = MIXER_DRAIN; exports.exchangeRates = exchangeRates; exports.PaintExchange = PaintExchange; exports.put255 = put255; exports.TileMedium = TileMedium; exports.DEFAULT_BLENDER = DEFAULT_BLENDER; exports.DEFAULT_SMUDGE = DEFAULT_SMUDGE; exports.normalizeBlender = normalizeBlender; exports.normalizeSmudge = normalizeSmudge; exports.blendRadius = blendRadius; exports.createBlenderScratch = createBlenderScratch; exports.blenderDab = blenderDab; exports.SmudgeTip = SmudgeTip; exports.DEFAULT_MIXER = DEFAULT_MIXER; exports.normalizeMixer = normalizeMixer; exports.mixerRates = mixerRates; exports.loadMixer = loadMixer; exports.cleanMixer = cleanMixer; exports.MixerStroke = MixerStroke;
2414
+ //# sourceMappingURL=chunk-3T5O225T.cjs.map