@pixerate/editor 0.240.0 → 0.241.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 (37) hide show
  1. package/dist/animation-editor/index.cjs +58 -58
  2. package/dist/animation-editor/index.js +3 -3
  3. package/dist/{brushstrokeRender-WB67GMUP.js → brushstrokeRender-MOYGEXOC.js} +2 -2
  4. package/dist/{brushstrokeRender-MPOM7BFW.cjs → brushstrokeRender-UKADOV5D.cjs} +5 -5
  5. package/dist/canvas/index.cjs +8 -8
  6. package/dist/canvas/index.js +1 -1
  7. package/dist/{chunk-NKTNLJUJ.cjs → chunk-5BR7LC24.cjs} +99 -11
  8. package/dist/{chunk-ZXNO3ZOX.cjs → chunk-7F4G74UQ.cjs} +35 -35
  9. package/dist/{chunk-AD7GA3TL.cjs → chunk-AFLMXITL.cjs} +23 -23
  10. package/dist/{chunk-PP6BXVRL.cjs → chunk-HGGJ36HR.cjs} +1 -1
  11. package/dist/{chunk-BBUE3UJL.cjs → chunk-HW72ABGR.cjs} +86 -86
  12. package/dist/{chunk-7YGBSFC6.js → chunk-ILM6GYAX.js} +1 -1
  13. package/dist/{chunk-THXFU3JB.cjs → chunk-KCMV2534.cjs} +2 -2
  14. package/dist/{chunk-3E36VDMJ.js → chunk-NE6OOKWJ.js} +1 -1
  15. package/dist/{chunk-RV5DKZHU.js → chunk-QP2B3MID.js} +1 -1
  16. package/dist/{chunk-UUBO6RB5.js → chunk-UDBDYZET.js} +1 -1
  17. package/dist/{chunk-PJYG6FCF.js → chunk-XBZK47LV.js} +98 -10
  18. package/dist/{chunk-XK4SST43.js → chunk-ZDPLZDTT.js} +1 -1
  19. package/dist/image-editor/index.cjs +775 -775
  20. package/dist/image-editor/index.js +4 -4
  21. package/dist/paint-editor/controller.d.cts +67 -0
  22. package/dist/paint-editor/controller.d.ts +67 -0
  23. package/dist/paint-editor/effect-paint.d.cts +60 -3
  24. package/dist/paint-editor/effect-paint.d.ts +60 -3
  25. package/dist/paint-editor/index.cjs +676 -430
  26. package/dist/paint-editor/index.js +282 -36
  27. package/dist/paint-editor/stickers.d.cts +6 -0
  28. package/dist/paint-editor/stickers.d.ts +6 -0
  29. package/dist/paint-editor/thick.d.cts +4 -0
  30. package/dist/paint-editor/thick.d.ts +4 -0
  31. package/dist/paint-editor/wet.d.cts +8 -0
  32. package/dist/paint-editor/wet.d.ts +8 -0
  33. package/dist/vector/index.cjs +962 -962
  34. package/dist/vector/index.js +4 -4
  35. package/package.json +1 -1
  36. package/skills/pixerate-paint-editor/SKILL.md +17 -21
  37. package/skills/pixerate-paint-editor/references/effect-paint.md +25 -0
@@ -5,7 +5,7 @@ import {
5
5
  moveAnchorHandle,
6
6
  subpathSegment,
7
7
  subpathSegmentCount
8
- } from "./chunk-7YGBSFC6.js";
8
+ } from "./chunk-ILM6GYAX.js";
9
9
  import {
10
10
  segmentPoint
11
11
  } from "./chunk-WEIK3UYB.js";
@@ -36,12 +36,50 @@ import {
36
36
  } from "./chunk-F67XMVDI.js";
37
37
 
38
38
  // src/paint-editor/effect-paint.ts
39
+ var EFFECT_STRIP_HEIGHT = 256;
40
+ var EFFECT_MAP_REPEATS = ["repeat", "mirror", "clamp"];
41
+ function effectPaintSize(p, canvas) {
42
+ return p.map ? { width: p.map.length, height: EFFECT_STRIP_HEIGHT } : { width: canvas.width, height: canvas.height };
43
+ }
44
+ function readEffectStrokeMap(raw) {
45
+ if (raw === void 0 || raw === null || raw.by === "canvas") return void 0;
46
+ const r = raw;
47
+ if (r.by !== "stroke") throw new Error(`map.by ${JSON.stringify(r.by)} is not a mapping; use "canvas" (fixed to the canvas) or "stroke" (along each stroke).`);
48
+ const length = r.length === void 0 ? 300 : Number(r.length);
49
+ if (!(Number.isFinite(length) && length >= 16 && length <= 4096)) throw new Error(`map.length ${JSON.stringify(r.length)} is out of range; use 16..4096 px of stroke per repeat.`);
50
+ const repeat = r.repeat === void 0 ? "repeat" : r.repeat;
51
+ if (!EFFECT_MAP_REPEATS.includes(repeat)) throw new Error(`map.repeat ${JSON.stringify(r.repeat)} is not a repeat mode; use ${EFFECT_MAP_REPEATS.join(", ")}.`);
52
+ return { by: "stroke", length: Math.round(length), repeat };
53
+ }
54
+ function stripIndex(img, map, dab, x, y) {
55
+ const ca = Math.cos(dab.direction);
56
+ const sa = Math.sin(dab.direction);
57
+ const dx = x - dab.x;
58
+ const dy = y - dab.y;
59
+ let u = (dab.travel + dx * ca - dy * sa) / map.length;
60
+ if (map.repeat === "clamp") u = Math.min(1, Math.max(0, u));
61
+ else if (map.repeat === "mirror") {
62
+ const m = (u % 2 + 2) % 2;
63
+ u = m > 1 ? 2 - m : m;
64
+ } else u = (u % 1 + 1) % 1;
65
+ const v = Math.min(1, Math.max(0, ((dx * sa + dy * ca) / Math.max(0.5, dab.radius) + 1) / 2));
66
+ const px = Math.min(img.width - 1, Math.floor(u * img.width));
67
+ const py = Math.min(img.height - 1, Math.floor(v * img.height));
68
+ return (py * img.width + px) * 4;
69
+ }
39
70
  function effectPaintKey(p) {
40
- return JSON.stringify({ type: "effect", effect: p.effect, provider: p.provider, values: p.values, time: p.time });
71
+ return JSON.stringify({ type: "effect", effect: p.effect, provider: p.provider, values: p.values, time: p.time, ...p.map ? { map: p.map } : {} });
41
72
  }
42
73
  function isPaintEffectPaint(raw) {
43
74
  return Boolean(raw) && typeof raw === "object" && raw.type === "effect";
44
75
  }
76
+ function readMapLeniently(raw) {
77
+ try {
78
+ return readEffectStrokeMap(raw);
79
+ } catch {
80
+ return void 0;
81
+ }
82
+ }
45
83
  function readPaintEffectPaint(raw) {
46
84
  if (!isPaintEffectPaint(raw)) return null;
47
85
  const r = raw;
@@ -51,11 +89,27 @@ function readPaintEffectPaint(raw) {
51
89
  effect: r.effect,
52
90
  provider: typeof r.provider === "string" ? r.provider : "",
53
91
  values: JSON.parse(JSON.stringify(r.values)),
54
- time: typeof r.time === "number" && Number.isFinite(r.time) && r.time > 0 ? r.time : 0
92
+ time: typeof r.time === "number" && Number.isFinite(r.time) && r.time > 0 ? r.time : 0,
93
+ ...readMapLeniently(r.map) ? { map: readMapLeniently(r.map) } : {},
94
+ ...r.live === true ? { live: true } : {}
55
95
  };
56
96
  }
57
- function sampleEffectField(img, rect, vals, field) {
97
+ function sampleEffectField(img, rect, vals, field, dab) {
58
98
  const { width, height, data } = img;
99
+ if (img.map && dab) {
100
+ for (let yy = 0, k = 0; yy < rect.height; yy++) {
101
+ for (let xx = 0; xx < rect.width; xx++, k++) {
102
+ const i = stripIndex(img, img.map, dab, rect.x + xx + 0.5, rect.y + yy + 0.5);
103
+ const f = k * 3;
104
+ const a = data[i + 3];
105
+ if (a < 255) vals[k] *= a / 255;
106
+ field[f] = data[i] / 255;
107
+ field[f + 1] = data[i + 1] / 255;
108
+ field[f + 2] = data[i + 2] / 255;
109
+ }
110
+ }
111
+ return;
112
+ }
59
113
  for (let yy = 0, k = 0; yy < rect.height; yy++) {
60
114
  const y = rect.y + yy;
61
115
  const inY = y >= 0 && y < height;
@@ -76,7 +130,14 @@ function sampleEffectField(img, rect, vals, field) {
76
130
  }
77
131
  }
78
132
  }
79
- function sampleEffectAt(img, x, y, out) {
133
+ function sampleEffectAt(img, x, y, out, dab) {
134
+ if (img.map && dab) {
135
+ const i2 = stripIndex(img, img.map, dab, x, y);
136
+ out[0] = img.data[i2] / 255;
137
+ out[1] = img.data[i2 + 1] / 255;
138
+ out[2] = img.data[i2 + 2] / 255;
139
+ return img.data[i2 + 3] / 255;
140
+ }
80
141
  const px = Math.floor(x);
81
142
  const py = Math.floor(y);
82
143
  if (px < 0 || py < 0 || px >= img.width || py >= img.height) return 0;
@@ -86,6 +147,20 @@ function sampleEffectAt(img, x, y, out) {
86
147
  out[2] = img.data[i + 2] / 255;
87
148
  return img.data[i + 3] / 255;
88
149
  }
150
+ function effectDabField(img, rect, cov, centre, mixed, field, steps = 0, dab = null) {
151
+ sampleEffectField(img, rect, cov, field, dab);
152
+ const d0 = mixed[0] - centre[0];
153
+ const d1 = mixed[1] - centre[1];
154
+ const d2 = mixed[2] - centre[2];
155
+ const n = rect.width * rect.height;
156
+ for (let k = 0; k < n; k++) {
157
+ if (steps) cov[k] = Math.round(cov[k] * steps) / steps;
158
+ const f = k * 3;
159
+ field[f] = Math.min(1, Math.max(0, field[f] + d0));
160
+ field[f + 1] = Math.min(1, Math.max(0, field[f + 1] + d1));
161
+ field[f + 2] = Math.min(1, Math.max(0, field[f + 2] + d2));
162
+ }
163
+ }
89
164
 
90
165
  // src/paint-editor/paper.ts
91
166
  var PAPER_FAMILIES = ["watercolor", "drawing", "pastel", "canvas", "panel", "smooth"];
@@ -490,7 +565,7 @@ var DryStroke = class {
490
565
  if (this.options.effect) {
491
566
  if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
492
567
  field = this.field.subarray(0, n * 3);
493
- sampleEffectField(this.options.effect, rect, vals, field);
568
+ sampleEffectField(this.options.effect, rect, vals, field, d);
494
569
  }
495
570
  this.coverage.accumulate(rect, vals, d.opacity, null, field);
496
571
  rects.push(rect);
@@ -692,6 +767,10 @@ var StickerSpray = class {
692
767
  const a = rand01(seed, i, 53) * Math.PI * 2;
693
768
  const dist = rand01(seed, i, 55) * off;
694
769
  const base = this.colorAt(d, Math.cos(a) * dist, Math.sin(a) * dist);
770
+ if (!(base.alpha > 0)) {
771
+ this.next++;
772
+ return;
773
+ }
695
774
  const sticker = {
696
775
  id: `st${this.next++}`,
697
776
  stamp: st.stamps[Math.floor(rand01(seed, i, 57) * st.stamps.length)] ?? "dot",
@@ -710,6 +789,12 @@ var StickerSpray = class {
710
789
  sampler;
711
790
  /** A sticker's colour: the paint colour, or gradient paint at its dab (`dx`, `dy` from the dab's centre). */
712
791
  colorAt(d, dx, dy) {
792
+ const effect = this.init.effect;
793
+ if (effect) {
794
+ const rgb = [0, 0, 0];
795
+ const alpha = sampleEffectAt(effect, d.x + dx, d.y + dy, rgb, d);
796
+ return { color: "#" + rgb.map((c) => Math.round(c * 255).toString(16).padStart(2, "0")).join(""), alpha };
797
+ }
713
798
  const g = this.init.paint;
714
799
  if (!g) return { color: this.init.color, alpha: 1 };
715
800
  this.sampler ??= new PaintGradientSampler(g, this.init.seed);
@@ -841,10 +926,8 @@ var MarkStroke = class {
841
926
  return rects;
842
927
  }
843
928
  step(copy, d, rects) {
844
- if (this.sampler) {
845
- this.pen = d;
846
- if (!this.tipTable) this.sampler.dab(d, this.penColor);
847
- }
929
+ this.pen = d;
930
+ if (this.sampler && !this.tipTable) this.sampler.dab(d, this.penColor);
848
931
  this.advance(copy, d, rects);
849
932
  }
850
933
  /** The colour of a mark at (x, y): its pen dab's, or across the tip by its offset from the pen. */
@@ -887,7 +970,7 @@ var MarkStroke = class {
887
970
  if (this.options.effect) {
888
971
  if (this.field.length < n * 3) this.field = new Float32Array(n * 3);
889
972
  const field = this.field.subarray(0, n * 3);
890
- sampleEffectField(this.options.effect, rect, vals, field);
973
+ sampleEffectField(this.options.effect, rect, vals, field, this.pen);
891
974
  this.coverage.accumulate(rect, vals, dab.opacity, null, field);
892
975
  } else {
893
976
  const color = this.markColor(x, y);
@@ -1888,11 +1971,16 @@ function parseBrushLibrary(raw, problems = []) {
1888
1971
  }
1889
1972
 
1890
1973
  export {
1974
+ EFFECT_STRIP_HEIGHT,
1975
+ EFFECT_MAP_REPEATS,
1976
+ effectPaintSize,
1977
+ readEffectStrokeMap,
1891
1978
  effectPaintKey,
1892
1979
  isPaintEffectPaint,
1893
1980
  readPaintEffectPaint,
1894
1981
  sampleEffectField,
1895
1982
  sampleEffectAt,
1983
+ effectDabField,
1896
1984
  PAPER_FAMILIES,
1897
1985
  PAPER_LIBRARY,
1898
1986
  DEFAULT_PAPER,
@@ -18,7 +18,7 @@ import {
18
18
  unionBounds,
19
19
  vectorGradientPoints,
20
20
  widthProfileAt
21
- } from "./chunk-7YGBSFC6.js";
21
+ } from "./chunk-ILM6GYAX.js";
22
22
  import {
23
23
  segmentLength,
24
24
  segmentParamAtLength,