@pixerate/editor 0.275.1 → 0.276.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 (65) hide show
  1. package/dist/animation-editor/index.cjs +62 -62
  2. package/dist/animation-editor/index.js +6 -6
  3. package/dist/brushes/index.cjs +81 -70
  4. package/dist/brushes/index.js +17 -6
  5. package/dist/brushes/patch.d.cts +1 -1
  6. package/dist/brushes/patch.d.ts +1 -1
  7. package/dist/brushes/profiles.d.cts +2 -0
  8. package/dist/brushes/profiles.d.ts +2 -0
  9. package/dist/brushes/schema.d.cts +1 -0
  10. package/dist/brushes/schema.d.ts +1 -0
  11. package/dist/brushes/types.d.cts +22 -1
  12. package/dist/brushes/types.d.ts +22 -1
  13. package/dist/{brushstrokeRender-WGY4DZHF.js → brushstrokeRender-FK3ASUUJ.js} +5 -5
  14. package/dist/{brushstrokeRender-2RQL46WB.cjs → brushstrokeRender-I6MYFCOU.cjs} +21 -21
  15. package/dist/canvas/index.cjs +13 -13
  16. package/dist/canvas/index.js +1 -1
  17. package/dist/{chunk-Y2ISSVPH.js → chunk-4EM5NGCL.js} +167 -9
  18. package/dist/{chunk-Y354ETAE.cjs → chunk-5HR5CSLC.cjs} +49 -49
  19. package/dist/{chunk-LMG57L5A.js → chunk-CTHWXW5M.js} +121 -8
  20. package/dist/{chunk-43XJC3PP.js → chunk-CVBTJVRN.js} +1 -1
  21. package/dist/{chunk-RJGW3RQS.js → chunk-GHYJAMMS.js} +1 -1
  22. package/dist/{chunk-NIBFKXMD.cjs → chunk-IFWLZZCW.cjs} +619 -2
  23. package/dist/{chunk-G5TCMKX5.js → chunk-IJ6NSWKM.js} +2 -2
  24. package/dist/{chunk-C6VB7CPY.cjs → chunk-ISCAJBPQ.cjs} +1 -1
  25. package/dist/{chunk-W6JDUXP3.js → chunk-J23JOHPR.js} +1 -1
  26. package/dist/{chunk-IEEY5RJV.js → chunk-JNMNM3SL.js} +1 -1
  27. package/dist/{chunk-HVO6XCI4.js → chunk-JPB65KQ6.js} +1 -1
  28. package/dist/{chunk-ACTHSQOI.js → chunk-LDZOXESA.js} +618 -1
  29. package/dist/{chunk-DHC4E2DU.cjs → chunk-MITQGHN3.cjs} +121 -8
  30. package/dist/{chunk-STLTOISW.cjs → chunk-NPPMKMCU.cjs} +17 -17
  31. package/dist/{chunk-75SYKUOT.js → chunk-OS6OG7DN.js} +1 -1
  32. package/dist/{chunk-OLZXXAER.cjs → chunk-OY24XRYF.cjs} +408 -250
  33. package/dist/{chunk-6QNDGMK7.cjs → chunk-Q33V5PEF.cjs} +123 -42
  34. package/dist/{chunk-JDXWLCXF.cjs → chunk-QNNZA3V5.cjs} +2 -2
  35. package/dist/{chunk-T6KNJVBP.cjs → chunk-U26LUXCJ.cjs} +35 -35
  36. package/dist/{chunk-62MFUHSI.cjs → chunk-UQXULAMQ.cjs} +16 -16
  37. package/dist/{chunk-RAPNRK4R.cjs → chunk-X7XBEX3O.cjs} +88 -88
  38. package/dist/{chunk-MSWLJI4G.js → chunk-XMSH66VU.js} +2 -2
  39. package/dist/{chunk-ZM7IKQ4X.js → chunk-XVPQNX7O.js} +89 -8
  40. package/dist/{chunk-YYZC2EQO.cjs → chunk-ZIE6EJGR.cjs} +23 -23
  41. package/dist/image-editor/index.cjs +880 -852
  42. package/dist/image-editor/index.js +36 -8
  43. package/dist/paint/index.cjs +33 -5
  44. package/dist/paint/index.d.cts +1 -0
  45. package/dist/paint/index.d.ts +1 -0
  46. package/dist/paint/index.js +32 -4
  47. package/dist/paint/live-patterns.d.cts +149 -0
  48. package/dist/paint/live-patterns.d.ts +149 -0
  49. package/dist/paint/wet/world.d.cts +13 -0
  50. package/dist/paint/wet/world.d.ts +13 -0
  51. package/dist/paint-editor/brush-model.d.cts +7 -1
  52. package/dist/paint-editor/brush-model.d.ts +7 -1
  53. package/dist/paint-editor/controller.d.cts +52 -0
  54. package/dist/paint-editor/controller.d.ts +52 -0
  55. package/dist/paint-editor/index.cjs +850 -510
  56. package/dist/paint-editor/index.js +355 -15
  57. package/dist/paint-editor/ui.d.cts +5 -1
  58. package/dist/paint-editor/ui.d.ts +5 -1
  59. package/dist/paint-editor/wet-tools.d.cts +42 -0
  60. package/dist/paint-editor/wet-tools.d.ts +42 -0
  61. package/dist/paint-editor/wet.d.cts +22 -1
  62. package/dist/paint-editor/wet.d.ts +22 -1
  63. package/dist/vector/index.cjs +1002 -1002
  64. package/dist/vector/index.js +7 -7
  65. package/package.json +1 -1
@@ -14,7 +14,7 @@ import {
14
14
  mergeBrushPatch,
15
15
  multiColorSliders,
16
16
  patchBrushValue
17
- } from "../chunk-G5TCMKX5.js";
17
+ } from "../chunk-IJ6NSWKM.js";
18
18
  import {
19
19
  createStickerTools,
20
20
  describeStickerRef
@@ -22,7 +22,7 @@ import {
22
22
  import {
23
23
  BRUSH_PREVIEW_DARK_PAPER,
24
24
  brushPreviewsOnDark
25
- } from "../chunk-RJGW3RQS.js";
25
+ } from "../chunk-GHYJAMMS.js";
26
26
  import {
27
27
  toVideoFfmpeg
28
28
  } from "../chunk-S2XPXA47.js";
@@ -42,7 +42,7 @@ import {
42
42
  resolveExternalAssets,
43
43
  serializeLayers,
44
44
  serializeLayersExternal
45
- } from "../chunk-MSWLJI4G.js";
45
+ } from "../chunk-XMSH66VU.js";
46
46
  import "../chunk-ADMRNWR7.js";
47
47
  import "../chunk-WJ4WQJCC.js";
48
48
  import {
@@ -68,7 +68,7 @@ import "../chunk-ZIF4UJJ6.js";
68
68
  import {
69
69
  createPigmentTools,
70
70
  pointerPressure
71
- } from "../chunk-HVO6XCI4.js";
71
+ } from "../chunk-JPB65KQ6.js";
72
72
  import "../chunk-VWEWY4FY.js";
73
73
  import "../chunk-HVQVHUTV.js";
74
74
  import {
@@ -125,7 +125,7 @@ import {
125
125
  wetPaperInput,
126
126
  wetPigmentOf,
127
127
  withStrokePath
128
- } from "../chunk-Y2ISSVPH.js";
128
+ } from "../chunk-4EM5NGCL.js";
129
129
  import {
130
130
  BRUSH_ID,
131
131
  BRUSH_PRESETS,
@@ -198,7 +198,7 @@ import {
198
198
  readBrushSource,
199
199
  resolveBrush,
200
200
  usesPaintExchange
201
- } from "../chunk-ZM7IKQ4X.js";
201
+ } from "../chunk-XVPQNX7O.js";
202
202
  import {
203
203
  STAMP_KINDS,
204
204
  StickersSlot,
@@ -248,15 +248,18 @@ import {
248
248
  normalizeWetParams,
249
249
  pigmentOf,
250
250
  wetSupport
251
- } from "../chunk-LMG57L5A.js";
252
- import "../chunk-W6JDUXP3.js";
251
+ } from "../chunk-CTHWXW5M.js";
252
+ import "../chunk-J23JOHPR.js";
253
253
  import {
254
254
  CoverageMap,
255
+ DEFAULT_LIVE_PATTERNS,
255
256
  DEFAULT_PAINT_GRADIENT,
256
257
  DEFAULT_PAINT_GRADIENT_MAPPING,
257
258
  EMPTY_MIXER_LOAD,
258
259
  GRAIN_PATTERNS,
259
260
  GRAIN_TILE,
261
+ LIVE_PATTERN_KINDS,
262
+ LIVE_PATTERN_PRESETS,
260
263
  MAX_BRUSH_SIZE,
261
264
  PAINT_COLOR_JSON_SCHEMA,
262
265
  PAINT_GRADIENT_FULL_SPEED,
@@ -288,6 +291,7 @@ import {
288
291
  isGradientPaint,
289
292
  loadMixer,
290
293
  normalizeHexColor,
294
+ normalizeLivePatterns,
291
295
  paintGradientMappingFor,
292
296
  paintSourceColor,
293
297
  paintSourceCss,
@@ -300,7 +304,7 @@ import {
300
304
  scalePaintGradient,
301
305
  solidPaint,
302
306
  updatePaintGradientStop
303
- } from "../chunk-ACTHSQOI.js";
307
+ } from "../chunk-LDZOXESA.js";
304
308
  import {
305
309
  TileableNoise,
306
310
  hash2,
@@ -5091,6 +5095,26 @@ async function replayPainting(source, target, options = {}) {
5091
5095
  if (e.layerId && target.getLayer(e.layerId)) target.wetPaper({ layerId: e.layerId, rect: sr(d.rect), amount: d.amount });
5092
5096
  break;
5093
5097
  }
5098
+ case "live-patterns": {
5099
+ const d = e.data;
5100
+ if (e.layerId && target.getLayer(e.layerId)) {
5101
+ target.setLivePatterns(e.layerId, d ? { ...d, scale: Math.min(4, d.scale * s), enabled: true } : { enabled: false });
5102
+ }
5103
+ break;
5104
+ }
5105
+ case "seed-live-patterns": {
5106
+ const d = e.data;
5107
+ if (e.layerId && target.getLayer(e.layerId) && Array.isArray(d.points)) {
5108
+ target.seedLivePatterns({
5109
+ layerId: e.layerId,
5110
+ points: d.points.map((p) => ({ ...p, x: p.x * s, y: p.y * s })),
5111
+ radius: Math.min(512, d.radius * s),
5112
+ seed: d.seed,
5113
+ ...d.settings ? { ...d.settings, ...d.settings.scale ? { scale: Math.min(4, d.settings.scale * s) } : {} } : {}
5114
+ });
5115
+ }
5116
+ break;
5117
+ }
5094
5118
  case "ghost-release": {
5095
5119
  const d = e.data;
5096
5120
  if (e.layerId && target.getLayer(e.layerId)) target.wet.releaseGhost(e.layerId, d?.linger);
@@ -6410,6 +6434,16 @@ var PaintEditorController = class _PaintEditorController {
6410
6434
  for (const [id, surface] of lights) if (findLayer(this.doc.layers, id)) light = withLayerLight(light, id, surface);
6411
6435
  this.doc = { ...this.doc, ...light ? { light } : {} };
6412
6436
  },
6437
+ layerHeight: (id) => this.doc.thick?.[id]?.height ?? null,
6438
+ updateHeights: (heights) => {
6439
+ let thick = this.doc.thick;
6440
+ for (const [id, height] of heights) {
6441
+ if (!findLayer(this.doc.layers, id)) continue;
6442
+ const prev = thick?.[id] ?? emptyThickChannels();
6443
+ thick = { ...thick ?? {}, [id]: { ...prev, height } };
6444
+ }
6445
+ if (thick !== this.doc.thick) this.doc = { ...this.doc, ...thick ? { thick } : {} };
6446
+ },
6413
6447
  changed: () => {
6414
6448
  }
6415
6449
  },
@@ -6860,7 +6894,7 @@ var PaintEditorController = class _PaintEditorController {
6860
6894
  this.extensions = {};
6861
6895
  this.guides = { ...this.guides, symmetry: { ...this.guides.symmetry, center: { x: next.canvas.width / 2, y: next.canvas.height / 2 } }, tracing: null };
6862
6896
  this.tracingVersion++;
6863
- if (this.wet.world) {
6897
+ if (this.wet.world || this.wet.patterns.size) {
6864
6898
  this.beginWetOp();
6865
6899
  this.wet.reset();
6866
6900
  }
@@ -8823,7 +8857,19 @@ var PaintEditorController = class _PaintEditorController {
8823
8857
  s.result.wet = this.wet.isWet(s.layerId);
8824
8858
  }
8825
8859
  const l = this.getLayer(s.layerId);
8826
- const thick = withLayerThick(this.doc.thick, l.id, s.thickChannels());
8860
+ let ch = s.thickChannels();
8861
+ if (wetStroke) {
8862
+ const f = this.wet.patterns.get(l.id);
8863
+ if (f?.enabled) {
8864
+ this.wet.settingsOf(l.id).livePatterns = { ...f.settings };
8865
+ if (f.settings.relief > 0 && f.tiles.size > 0) {
8866
+ const prevCh = ch ?? this.doc.thick?.[l.id] ?? emptyThickChannels();
8867
+ const nextHeight = f.applyReliefHeight(prevCh.height);
8868
+ if (nextHeight !== prevCh.height) ch = { ...prevCh, height: nextHeight };
8869
+ }
8870
+ }
8871
+ }
8872
+ const thick = withLayerThick(this.doc.thick, l.id, ch);
8827
8873
  const sprayed = s.stickers();
8828
8874
  const stickers = sprayed ? { ...this.doc.stickers ?? {}, [l.id]: [...this.doc.stickers?.[l.id] ?? [], ...sprayed] } : this.doc.stickers;
8829
8875
  const wetLight = wetStroke && this.wet.world?.hasLane(l.id) ? this.wet.world.lightDisplay(l.id) : null;
@@ -8976,7 +9022,19 @@ var PaintEditorController = class _PaintEditorController {
8976
9022
  }
8977
9023
  const l = findLayer(out.layers, s.layerId).layer;
8978
9024
  const offset = s.init.wet ? { x: 0, y: 0 } : l.offset;
8979
- const thick = withLayerThick(out.thick, l.id, s.thickChannels());
9025
+ let ch = s.thickChannels();
9026
+ if (s.init.wet) {
9027
+ const f = this.wet.patterns.get(l.id);
9028
+ if (f?.enabled) {
9029
+ this.wet.settingsOf(l.id).livePatterns = { ...f.settings };
9030
+ if (f.settings.relief > 0 && f.tiles.size > 0) {
9031
+ const prevCh = ch ?? out.thick?.[l.id] ?? emptyThickChannels();
9032
+ const nextHeight = f.applyReliefHeight(prevCh.height);
9033
+ if (nextHeight !== prevCh.height) ch = { ...prevCh, height: nextHeight };
9034
+ }
9035
+ }
9036
+ }
9037
+ const thick = withLayerThick(out.thick, l.id, ch);
8980
9038
  const sprayed = s.stickers();
8981
9039
  const lit = s.lightChannel() ?? (s.init.wet && this.wet.world?.hasLane(l.id) ? this.wet.world.lightDisplay(l.id) : null);
8982
9040
  const fl = s.fluorChannel();
@@ -9640,6 +9698,149 @@ var PaintEditorController = class _PaintEditorController {
9640
9698
  if (this.wet.busy) return [];
9641
9699
  return this.wet.waterOverlay(rect);
9642
9700
  }
9701
+ layerLivePatternsInfo(layerId) {
9702
+ const info = this.wet.info(layerId);
9703
+ const stats = this.wet.livePatternStats(layerId);
9704
+ return {
9705
+ layerId,
9706
+ wet: info.wet,
9707
+ wetCells: info.wetCells,
9708
+ wetBounds: info.wetBounds,
9709
+ ...stats
9710
+ };
9711
+ }
9712
+ checkLivePatternsInput(input) {
9713
+ if (!input) return {};
9714
+ const out = {};
9715
+ if (input.pattern !== void 0) {
9716
+ if (!LIVE_PATTERN_KINDS.includes(input.pattern)) {
9717
+ throw new PaintEditorError(`pattern must be one of ${LIVE_PATTERN_KINDS.join(", ")}, not ${JSON.stringify(input.pattern)}.`);
9718
+ }
9719
+ out.pattern = input.pattern;
9720
+ }
9721
+ if (input.feed !== void 0) {
9722
+ if (!(Number.isFinite(input.feed) && input.feed >= 5e-3 && input.feed <= 0.1)) {
9723
+ throw new PaintEditorError(`feed ${input.feed} is out of range; use 0.005..0.1.`);
9724
+ }
9725
+ out.feed = input.feed;
9726
+ }
9727
+ if (input.kill !== void 0) {
9728
+ if (!(Number.isFinite(input.kill) && input.kill >= 0.02 && input.kill <= 0.08)) {
9729
+ throw new PaintEditorError(`kill ${input.kill} is out of range; use 0.02..0.08.`);
9730
+ }
9731
+ out.kill = input.kill;
9732
+ }
9733
+ if (input.scale !== void 0) {
9734
+ if (!(Number.isFinite(input.scale) && input.scale >= 0.25 && input.scale <= 4)) {
9735
+ throw new PaintEditorError(`scale ${input.scale} is out of range; use 0.25..4.`);
9736
+ }
9737
+ out.scale = input.scale;
9738
+ }
9739
+ if (input.contrast !== void 0) {
9740
+ if (!(Number.isFinite(input.contrast) && input.contrast >= 0 && input.contrast <= 2)) {
9741
+ throw new PaintEditorError(`contrast ${input.contrast} is out of range; use 0..2.`);
9742
+ }
9743
+ out.contrast = input.contrast;
9744
+ }
9745
+ if (input.secondColor !== void 0) {
9746
+ out.secondColor = input.secondColor === null ? null : parseColor(input.secondColor, "secondColor");
9747
+ }
9748
+ if (input.relief !== void 0) {
9749
+ if (!(Number.isFinite(input.relief) && input.relief >= 0 && input.relief <= 1)) {
9750
+ throw new PaintEditorError(`relief ${input.relief} is out of range; use 0..1.`);
9751
+ }
9752
+ out.relief = input.relief;
9753
+ }
9754
+ return out;
9755
+ }
9756
+ /**
9757
+ * Reads a layer's Gray-Scott reaction-diffusion live patterns (`layerId`, default the active
9758
+ * layer), plus all layers with wet paint or live patterns and the built-in Pearson presets.
9759
+ */
9760
+ getLivePatterns(layerId) {
9761
+ const l = this.wetLayer(layerId, "read the live patterns of");
9762
+ const primary = this.layerLivePatternsInfo(l.id);
9763
+ const ids = /* @__PURE__ */ new Set([l.id, ...this.wet.wetLayerIds()]);
9764
+ for (const [id, f] of this.wet.patterns) {
9765
+ if ((f.enabled || f.tiles.size > 0) && findLayer(this.doc.layers, id)) ids.add(id);
9766
+ }
9767
+ for (const [id, s] of this.wet.settings) {
9768
+ if (s.livePatterns && findLayer(this.doc.layers, id)) ids.add(id);
9769
+ }
9770
+ const layers = [...ids].map((id) => id === l.id ? primary : this.layerLivePatternsInfo(id));
9771
+ return {
9772
+ ...primary,
9773
+ layers,
9774
+ presets: LIVE_PATTERN_PRESETS,
9775
+ brushPresets: ["turing-wash", "coral-ink"]
9776
+ };
9777
+ }
9778
+ /**
9779
+ * Configures a layer's Gray-Scott reaction-diffusion live patterns (or clears them with
9780
+ * `enabled: false`). One undo step.
9781
+ */
9782
+ setLivePatterns(layerId, update) {
9783
+ this.requireIdle("changing live patterns");
9784
+ this.requireWetIdle("changing live patterns");
9785
+ const l = this.wetLayer(layerId, "set the live patterns of");
9786
+ if (isLayerLocked(l, "pixels")) throw new PaintEditorError(`Layer "${l.name}" has its pixels locked; unlock it first.`);
9787
+ if (update.enabled !== void 0 && typeof update.enabled !== "boolean") {
9788
+ throw new PaintEditorError("enabled must be true or false.");
9789
+ }
9790
+ const checked = this.checkLivePatternsInput(update);
9791
+ const before = this.doc;
9792
+ this.beginWetOp();
9793
+ const next = this.wet.setLivePatterns(l.id, update.enabled === false ? null : checked);
9794
+ this.logWet({ kind: "live-patterns", layerId: l.id, data: next });
9795
+ this.commit("Live patterns", this.withWetEvents(this.doc), before);
9796
+ return this.layerLivePatternsInfo(l.id);
9797
+ }
9798
+ /**
9799
+ * Seeds Gray-Scott reaction-diffusion activator (`V`) discs into a layer's live-pattern field so
9800
+ * patterns grow where the layer holds wet water and freeze where it dries. One undo step.
9801
+ */
9802
+ seedLivePatterns(options) {
9803
+ this.requireIdle("seeding live patterns");
9804
+ this.requireWetIdle("seeding live patterns");
9805
+ const l = this.wetLayer(options.layerId, "seed live patterns on");
9806
+ if (isLayerLocked(l, "pixels")) throw new PaintEditorError(`Layer "${l.name}" has its pixels locked; unlock it first.`);
9807
+ if (!Array.isArray(options.points) || !options.points.length) {
9808
+ throw new PaintEditorError("points is empty; give at least one {x, y} in document pixels.");
9809
+ }
9810
+ const { width, height } = this.doc.canvas;
9811
+ const points = options.points.map((p, i) => {
9812
+ if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y)) {
9813
+ throw new PaintEditorError(`points[${i}] needs finite x and y in document pixels.`);
9814
+ }
9815
+ if (p.x < 0 || p.y < 0 || p.x >= width || p.y >= height) {
9816
+ throw new PaintEditorError(`points[${i}] (${p.x}, ${p.y}) is outside the ${width}\xD7${height} canvas.`);
9817
+ }
9818
+ if (p.strength !== void 0 && !(Number.isFinite(p.strength) && p.strength > 0 && p.strength <= 1)) {
9819
+ throw new PaintEditorError(`points[${i}].strength ${p.strength} is out of range; use above 0 up to 1.`);
9820
+ }
9821
+ return { x: p.x, y: p.y, ...p.strength !== void 0 ? { strength: p.strength } : {} };
9822
+ });
9823
+ const radius = options.radius ?? 16;
9824
+ if (!(Number.isFinite(radius) && radius >= 1 && radius <= 512)) {
9825
+ throw new PaintEditorError(`radius ${radius} is out of range; use 1..512 px.`);
9826
+ }
9827
+ const seed = options.seed ?? mix32(this.seedCounter++ * 2654435761 >>> 0) & 2147483647;
9828
+ if (!Number.isInteger(seed) || seed < 0) {
9829
+ throw new PaintEditorError(`seed ${seed} must be a whole number of 0 or more.`);
9830
+ }
9831
+ const hasSettings = options.pattern !== void 0 || options.feed !== void 0 || options.kill !== void 0 || options.scale !== void 0 || options.contrast !== void 0 || options.secondColor !== void 0 || options.relief !== void 0;
9832
+ const settings = hasSettings ? this.checkLivePatternsInput(options) : void 0;
9833
+ const before = this.doc;
9834
+ this.beginWetOp();
9835
+ const { seededCells } = this.wet.seedLivePatterns(l.id, points, radius, seed, settings);
9836
+ this.logWet({
9837
+ kind: "seed-live-patterns",
9838
+ layerId: l.id,
9839
+ data: { points, radius, seed, ...settings ? { settings } : {} }
9840
+ });
9841
+ this.commit("Seed live patterns", this.withWetEvents(this.doc), before);
9842
+ return { ...this.layerLivePatternsInfo(l.id), seededCells };
9843
+ }
9643
9844
  // --- Rendering -----------------------------------------------------------------
9644
9845
  sources() {
9645
9846
  return { width: this.doc.canvas.width, height: this.doc.canvas.height };
@@ -9895,7 +10096,12 @@ var PaintEditorController = class _PaintEditorController {
9895
10096
  const savedGhost = ghost?.trigger === "agent" ? null : ghost;
9896
10097
  const heldEvap = this.heldEvaporation.get(id);
9897
10098
  const params = heldEvap !== void 0 ? { ...set.params, evaporation: heldEvap } : { ...set.params };
9898
- layers[id] = { params, flow: { ...set.flow }, ...savedGhost ? { ghost: savedGhost } : {} };
10099
+ layers[id] = {
10100
+ params,
10101
+ flow: { ...set.flow },
10102
+ ...savedGhost ? { ghost: savedGhost } : {},
10103
+ ...set.livePatterns ? { livePatterns: { ...set.livePatterns } } : {}
10104
+ };
9899
10105
  }
9900
10106
  const flat = this.wet.tilt.angle === 0;
9901
10107
  if (!Object.keys(layers).length && flat) return void 0;
@@ -9914,7 +10120,14 @@ var PaintEditorController = class _PaintEditorController {
9914
10120
  }
9915
10121
  const x = v;
9916
10122
  const ghost = readWetGhost(x.ghost);
9917
- this.wet.settings.set(id, { params: normalizeWetParams(x.params ?? {}), flow: normalizeFlow(x.flow ?? {}), ...ghost ? { ghost } : {} });
10123
+ const livePatterns = x.livePatterns ? normalizeLivePatterns(x.livePatterns) : null;
10124
+ this.wet.settings.set(id, {
10125
+ params: normalizeWetParams(x.params ?? {}),
10126
+ flow: normalizeFlow(x.flow ?? {}),
10127
+ ...ghost ? { ghost } : {},
10128
+ ...livePatterns ? { livePatterns } : {}
10129
+ });
10130
+ if (livePatterns) this.wet.patternFieldOf(id).configure(livePatterns);
9918
10131
  }
9919
10132
  }
9920
10133
  }
@@ -12102,6 +12315,9 @@ function shiftDocument(doc, dx, dy) {
12102
12315
  if (e.kind === "dry" && typeof d.x === "number") return { ...e, data: moveRect(d, dx, dy) };
12103
12316
  if (e.kind === "wet-paper" && d.rect) return { ...e, data: { ...d, rect: moveRect(d.rect, dx, dy) } };
12104
12317
  if (e.kind === "flow" && d.center) return { ...e, data: { ...d, center: movePoint(d.center, dx, dy) } };
12318
+ if (e.kind === "seed-live-patterns" && Array.isArray(d.points)) {
12319
+ return { ...e, data: { ...d, points: d.points.map((p) => ({ ...p, x: p.x + dx, y: p.y + dy })) } };
12320
+ }
12105
12321
  return e;
12106
12322
  });
12107
12323
  }
@@ -13139,6 +13355,43 @@ var WET_PARAM_SCHEMA = {
13139
13355
  bloom: UNIT("Blooms: water running into a drying wash pushes its pigment into cauliflower edges (default 0.4)."),
13140
13356
  bleed: UNIT("Colour bleed between touching wet areas (default 0.3).")
13141
13357
  };
13358
+ var LIVE_PATTERN_SCHEMA = {
13359
+ pattern: {
13360
+ type: "string",
13361
+ enum: [...LIVE_PATTERN_KINDS],
13362
+ description: "Pearson Gray-Scott preset: spots, stripes, coral, maze or worms (default spots). Sets feed and kill unless explicit feed/kill are also passed."
13363
+ },
13364
+ feed: {
13365
+ type: "number",
13366
+ minimum: 5e-3,
13367
+ maximum: 0.1,
13368
+ description: "Gray-Scott feed rate F, 0.005..0.1 (default: the pattern preset\u2019s feed rate)."
13369
+ },
13370
+ kill: {
13371
+ type: "number",
13372
+ minimum: 0.02,
13373
+ maximum: 0.08,
13374
+ description: "Gray-Scott kill rate k, 0.02..0.08 (default: the pattern preset\u2019s kill rate)."
13375
+ },
13376
+ scale: {
13377
+ type: "number",
13378
+ minimum: 0.25,
13379
+ maximum: 4,
13380
+ description: "Diffusion scale 0.25..4 (larger values grow broader pattern features; default 1)."
13381
+ },
13382
+ contrast: {
13383
+ type: "number",
13384
+ minimum: 0,
13385
+ maximum: 2,
13386
+ description: "How strongly V modulates the wet pigment or blends toward secondColor, 0..2 (default 0.85)."
13387
+ },
13388
+ secondColor: {
13389
+ type: ["string", "null"],
13390
+ pattern: "^#[0-9a-fA-F]{6}$",
13391
+ description: "Optional #rrggbb colour that high-V pattern ridges blend toward, or null to modulate pigment contrast (default null)."
13392
+ },
13393
+ relief: UNIT("Impasto ridge height 0..1 written from V into the layer\u2019s thick height map so light catches the pattern (default 0).")
13394
+ };
13142
13395
  function createPaintWetTools(c) {
13143
13396
  const stillWet = () => c.wetState().wet;
13144
13397
  return [
@@ -13302,6 +13555,73 @@ function createPaintWetTools(c) {
13302
13555
  inputSchema: { type: "object", properties: { visible: { type: "boolean", description: "Show (true) or hide (false) the overlay." } }, required: ["visible"], additionalProperties: false },
13303
13556
  annotations: { title: "Show the water", ...SET2 },
13304
13557
  handler: ({ visible }) => ({ visible: c.setWaterOverlay(visible), stillWet: stillWet() })
13558
+ },
13559
+ {
13560
+ name: "get_live_patterns",
13561
+ title: "Get live patterns",
13562
+ description: "Reads the Gray-Scott reaction-diffusion live patterns on a layer (layerId, default the active layer): whether live patterns are enabled, the active settings (pattern, feed, kill, scale, contrast, secondColor, relief), activeTiles, patternCells, wetPatternCells (pattern cells still holding water where the pattern can grow), meanV, maxV, bounds, wet coverage (wet, wetCells, wetBounds), layers (every layer with wet paint or live patterns), and presets (built-in Pearson feed/kill presets: spots, stripes, coral, maze, worms). Call it before or after advance_wet_paint and dry_paint to inspect pattern growth and freezing.",
13563
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID2 }, additionalProperties: false },
13564
+ annotations: { title: "Get live patterns", ...READ3 },
13565
+ coordinates: { out: ["wetBounds", "bounds", "layers[].wetBounds", "layers[].bounds"] },
13566
+ handler: ({ layerId }) => c.getLivePatterns(layerId)
13567
+ },
13568
+ {
13569
+ name: "set_live_patterns",
13570
+ title: "Set live patterns",
13571
+ description: "Configures a layer\u2019s Gray-Scott reaction-diffusion live patterns (or clears them with enabled: false). Patterns grow inside wet paint during advance_wet_paint and freeze when the paint dries (dry_paint). Choose a Pearson preset (spots, stripes, coral, maze, worms) or explicit feed and kill rates, scale, contrast, secondColor (#rrggbb or null to modulate pigment contrast) and relief (0..1, writes ridge height into thick paint so lighting catches the pattern). One undo step. Returns the layer\u2019s live-pattern stats and stillWet.",
13572
+ inputSchema: {
13573
+ type: "object",
13574
+ properties: {
13575
+ layerId: LAYER_ID2,
13576
+ enabled: { type: "boolean", description: "true (default) configures live patterns on the layer; false clears them." },
13577
+ ...LIVE_PATTERN_SCHEMA
13578
+ },
13579
+ additionalProperties: false
13580
+ },
13581
+ annotations: { title: "Set live patterns", ...SET2 },
13582
+ coordinates: { out: ["wetBounds", "bounds"] },
13583
+ handler: async ({ layerId, ...update }) => {
13584
+ await c.wetIdle();
13585
+ return { ...c.setLivePatterns(layerId, update), stillWet: stillWet() };
13586
+ }
13587
+ },
13588
+ {
13589
+ name: "seed_live_patterns",
13590
+ title: "Seed live patterns",
13591
+ description: 'Seeds Gray-Scott reaction-diffusion activator (V) discs at points on a layer so reaction-diffusion patterns (spots, stripes, coral, maze, worms) grow where the layer holds wet water (advance_wet_paint) and freeze where it dries (dry_paint). Painting with a wet brush that has a livePatterns facet (such as preset "turing-wash" or "coral-ink") also seeds along the stroke automatically. One undo step. Returns seededCells, the layer\u2019s live-pattern stats (patternCells, wetPatternCells, meanV, maxV, bounds) and stillWet.',
13592
+ inputSchema: {
13593
+ type: "object",
13594
+ properties: {
13595
+ layerId: LAYER_ID2,
13596
+ points: {
13597
+ type: "array",
13598
+ minItems: 1,
13599
+ maxItems: 1024,
13600
+ description: "Seed centres in document pixels where reaction-diffusion activator V is nucleated.",
13601
+ items: {
13602
+ type: "object",
13603
+ properties: {
13604
+ x: { type: "number", description: "Document x in pixels." },
13605
+ y: { type: "number", description: "Document y in pixels." },
13606
+ strength: { type: "number", exclusiveMinimum: 0, maximum: 1, description: "Seed strength 0..1 (default 1)." }
13607
+ },
13608
+ required: ["x", "y"],
13609
+ additionalProperties: false
13610
+ }
13611
+ },
13612
+ radius: { type: "number", minimum: 1, maximum: 512, description: "Seed disc radius in document pixels (default 16)." },
13613
+ seed: { type: "integer", minimum: 0, description: "Deterministic noise seed for the nucleated discs (default: next editor seed)." },
13614
+ ...LIVE_PATTERN_SCHEMA
13615
+ },
13616
+ required: ["points"],
13617
+ additionalProperties: false
13618
+ },
13619
+ annotations: { title: "Seed live patterns", ...WRITE3 },
13620
+ coordinates: { in: ["points"], out: ["wetBounds", "bounds"] },
13621
+ handler: async (args) => {
13622
+ await c.wetIdle();
13623
+ return { ...c.seedLivePatterns(args), stillWet: stillWet() };
13624
+ }
13305
13625
  }
13306
13626
  ];
13307
13627
  }
@@ -15435,6 +15755,18 @@ var WET_TILT_SLIDERS = [
15435
15755
  { path: "direction", label: "Downhill", min: 0, max: 359, step: 1 }
15436
15756
  ];
15437
15757
  var WET_SPEED_SLIDER = { path: "speed", label: "Speed", min: 15, max: 480, step: 15 };
15758
+ var LIVE_PATTERN_LABELS = {
15759
+ spots: "Spots",
15760
+ stripes: "Stripes",
15761
+ coral: "Coral",
15762
+ maze: "Maze",
15763
+ worms: "Worms"
15764
+ };
15765
+ var LIVE_PATTERN_SLIDERS = [
15766
+ { path: "scale", label: "Scale", min: 0.25, max: 4, step: 0.05 },
15767
+ { path: "contrast", label: "Contrast", min: 0, max: 2, step: 0.05 },
15768
+ { path: "relief", label: "Relief", min: 0, max: 1, step: 0.01 }
15769
+ ];
15438
15770
  var WET_GPU_OPTIONS = [
15439
15771
  { value: "auto", label: "GPU for large washes" },
15440
15772
  { value: "off", label: "CPU only" }
@@ -16108,13 +16440,14 @@ function uiTables() {
16108
16440
  brushOptions: BRUSH_OPTION_CONTROLS,
16109
16441
  blendMenu: BLEND_MENU_GROUPS,
16110
16442
  layerEffects: LAYER_EFFECT_MENU.map(({ kind, label }) => ({ kind, label, controls: layerEffectControls(defaultLayerEffect(kind, kind)) })),
16111
- sliders: { paper: PAPER_SLIDERS, wet: WET_PARAM_SLIDERS, wetTilt: WET_TILT_SLIDERS, wetSpeed: WET_SPEED_SLIDER, light: LIGHT_SLIDERS, uvLight: UV_LIGHT_SLIDERS, viewTilt: VIEW_TILT_SLIDERS, ghost: GHOST_SLIDERS, mutation: MUTATION_SLIDERS, stroke: STROKE_SLIDERS, sticker: STICKER_SLIDERS },
16443
+ sliders: { paper: PAPER_SLIDERS, wet: WET_PARAM_SLIDERS, wetTilt: WET_TILT_SLIDERS, wetSpeed: WET_SPEED_SLIDER, light: LIGHT_SLIDERS, uvLight: UV_LIGHT_SLIDERS, viewTilt: VIEW_TILT_SLIDERS, ghost: GHOST_SLIDERS, mutation: MUTATION_SLIDERS, livePatterns: LIVE_PATTERN_SLIDERS, stroke: STROKE_SLIDERS, sticker: STICKER_SLIDERS },
16112
16444
  pathStrokeTapers: PATH_STROKE_TAPERS,
16113
16445
  padSampleSizes: PAD_SAMPLE_SIZES,
16114
16446
  wetGpu: WET_GPU_OPTIONS,
16115
16447
  labels: {
16116
16448
  wetTools: WET_TOOL_LABELS,
16117
16449
  wetMedia: WET_MEDIUM_LABELS,
16450
+ livePatterns: LIVE_PATTERN_LABELS,
16118
16451
  impastoTools: IMPASTO_TOOL_LABELS,
16119
16452
  particleKinds: PARTICLE_KIND_LABELS,
16120
16453
  particleRenders: PARTICLE_RENDER_LABELS,
@@ -16191,6 +16524,7 @@ export {
16191
16524
  DEFAULT_IRIDESCENCE,
16192
16525
  DEFAULT_LIGHT,
16193
16526
  DEFAULT_LIGHTING,
16527
+ DEFAULT_LIVE_PATTERNS,
16194
16528
  DEFAULT_MULTI_COLOR,
16195
16529
  DEFAULT_MUTATION,
16196
16530
  DEFAULT_MUTATION_RATES,
@@ -16235,6 +16569,11 @@ export {
16235
16569
  IMPASTO_TOOL_LABELS,
16236
16570
  IRIDESCENCE_SLIDERS,
16237
16571
  LIGHT_SLIDERS,
16572
+ LIVE_PATTERN_KINDS,
16573
+ LIVE_PATTERN_LABELS,
16574
+ LIVE_PATTERN_PRESETS,
16575
+ LIVE_PATTERN_SCHEMA,
16576
+ LIVE_PATTERN_SLIDERS,
16238
16577
  LIVE_STROKE_ENGINES,
16239
16578
  LitCache,
16240
16579
  MAX_BRUSH_SIZE,
@@ -16482,6 +16821,7 @@ export {
16482
16821
  normalizeImpasto,
16483
16822
  normalizeLight,
16484
16823
  normalizeLighting,
16824
+ normalizeLivePatterns,
16485
16825
  normalizeMultiColor,
16486
16826
  normalizeMutation,
16487
16827
  normalizeMutationRates,
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import type { MixerLoad } from '../paint/exchange.cjs';
12
12
  import { type BlendModeInfo } from '../raster/blend.cjs';
13
- import type { ImpastoTool, WetMediumKind, WetTool } from './brushes.cjs';
13
+ import type { ImpastoTool, WetMediumKind, WetTool, LivePatternKind } from './brushes.cjs';
14
14
  import type { FillRegionOptions, PaintEditorController } from './controller.cjs';
15
15
  import type { PenPoint } from './engine.cjs';
16
16
  import { type Point2 } from './guides.cjs';
@@ -204,6 +204,10 @@ export declare const WET_PARAM_SLIDERS: readonly SliderControl[];
204
204
  export declare const WET_TILT_SLIDERS: readonly SliderControl[];
205
205
  /** The speed slider (steps per second). */
206
206
  export declare const WET_SPEED_SLIDER: SliderControl;
207
+ /** Labels of the Gray-Scott live pattern kinds. */
208
+ export declare const LIVE_PATTERN_LABELS: Readonly<Record<LivePatternKind, string>>;
209
+ /** The wet panel's sliders for a layer's live patterns. */
210
+ export declare const LIVE_PATTERN_SLIDERS: readonly SliderControl[];
207
211
  /** The wet-engine choices a panel offers (Engine B, the WebGPU accelerator). */
208
212
  export declare const WET_GPU_OPTIONS: readonly {
209
213
  value: WetGpuMode;
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import type { MixerLoad } from '../paint/exchange.js';
12
12
  import { type BlendModeInfo } from '../raster/blend.js';
13
- import type { ImpastoTool, WetMediumKind, WetTool } from './brushes.js';
13
+ import type { ImpastoTool, WetMediumKind, WetTool, LivePatternKind } from './brushes.js';
14
14
  import type { FillRegionOptions, PaintEditorController } from './controller.js';
15
15
  import type { PenPoint } from './engine.js';
16
16
  import { type Point2 } from './guides.js';
@@ -204,6 +204,10 @@ export declare const WET_PARAM_SLIDERS: readonly SliderControl[];
204
204
  export declare const WET_TILT_SLIDERS: readonly SliderControl[];
205
205
  /** The speed slider (steps per second). */
206
206
  export declare const WET_SPEED_SLIDER: SliderControl;
207
+ /** Labels of the Gray-Scott live pattern kinds. */
208
+ export declare const LIVE_PATTERN_LABELS: Readonly<Record<LivePatternKind, string>>;
209
+ /** The wet panel's sliders for a layer's live patterns. */
210
+ export declare const LIVE_PATTERN_SLIDERS: readonly SliderControl[];
207
211
  /** The wet-engine choices a panel offers (Engine B, the WebGPU accelerator). */
208
212
  export declare const WET_GPU_OPTIONS: readonly {
209
213
  value: WetGpuMode;
@@ -63,4 +63,46 @@ export declare const WET_PARAM_SCHEMA: {
63
63
  readonly description: string;
64
64
  };
65
65
  };
66
+ export declare const LIVE_PATTERN_SCHEMA: {
67
+ readonly pattern: {
68
+ readonly type: "string";
69
+ readonly enum: readonly ["spots", "stripes", "coral", "maze", "worms"];
70
+ readonly description: "Pearson Gray-Scott preset: spots, stripes, coral, maze or worms (default spots). Sets feed and kill unless explicit feed/kill are also passed.";
71
+ };
72
+ readonly feed: {
73
+ readonly type: "number";
74
+ readonly minimum: 0.005;
75
+ readonly maximum: 0.1;
76
+ readonly description: "Gray-Scott feed rate F, 0.005..0.1 (default: the pattern preset’s feed rate).";
77
+ };
78
+ readonly kill: {
79
+ readonly type: "number";
80
+ readonly minimum: 0.02;
81
+ readonly maximum: 0.08;
82
+ readonly description: "Gray-Scott kill rate k, 0.02..0.08 (default: the pattern preset’s kill rate).";
83
+ };
84
+ readonly scale: {
85
+ readonly type: "number";
86
+ readonly minimum: 0.25;
87
+ readonly maximum: 4;
88
+ readonly description: "Diffusion scale 0.25..4 (larger values grow broader pattern features; default 1).";
89
+ };
90
+ readonly contrast: {
91
+ readonly type: "number";
92
+ readonly minimum: 0;
93
+ readonly maximum: 2;
94
+ readonly description: "How strongly V modulates the wet pigment or blends toward secondColor, 0..2 (default 0.85).";
95
+ };
96
+ readonly secondColor: {
97
+ readonly type: readonly ["string", "null"];
98
+ readonly pattern: "^#[0-9a-fA-F]{6}$";
99
+ readonly description: "Optional #rrggbb colour that high-V pattern ridges blend toward, or null to modulate pigment contrast (default null).";
100
+ };
101
+ readonly relief: {
102
+ readonly type: "number";
103
+ readonly minimum: 0;
104
+ readonly maximum: 1;
105
+ readonly description: string;
106
+ };
107
+ };
66
108
  export declare function createPaintWetTools(c: PaintEditorController): PaintAgentTool[];