@pixerate/editor 0.217.0 → 0.218.1

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.
@@ -32,6 +32,7 @@ import {
32
32
  SRGB_TO_LINEAR,
33
33
  WET_FLOW_KINDS,
34
34
  WET_PARAM_KEYS,
35
+ WET_TILE,
35
36
  WetMedium,
36
37
  WetWorld,
37
38
  createPigmentTools,
@@ -39,13 +40,14 @@ import {
39
40
  findWebGpu,
40
41
  flowField,
41
42
  linearToSrgb8,
43
+ moveWetSnapshot,
42
44
  normalizeFlow,
43
45
  normalizeTilt,
44
46
  normalizeWetParams,
45
47
  pigmentOf,
46
48
  pointerPressure,
47
49
  wetSupport
48
- } from "../chunk-WJ2RBZMM.js";
50
+ } from "../chunk-HLBTRSQJ.js";
49
51
  import "../chunk-HVQVHUTV.js";
50
52
  import {
51
53
  writeZip
@@ -2162,15 +2164,50 @@ var PaintWetLayers = class {
2162
2164
  if (this.world.laneIds().length) throw new Error("The wet world is the wrong size; dry the paint before resizing the canvas.");
2163
2165
  this.dropWorld();
2164
2166
  }
2165
- if (!this.world) {
2166
- this.world = new WetWorld(width, height, wetPaperInput(this.host.paper()), { kernel: this.options.kernel, threads: this.options.threads });
2167
- this.world.setTilt(this.tilt);
2168
- this.world.gpuMinTiles = this.gpuMinTiles;
2169
- if (this.gpu) this.world.attachGpu(this.gpu);
2170
- else this.startGpu();
2171
- }
2167
+ if (!this.world) this.world = this.makeWorld(width, height, this.host.paper());
2172
2168
  return this.world;
2173
2169
  }
2170
+ makeWorld(width, height, paper) {
2171
+ const world = new WetWorld(width, height, wetPaperInput(paper), { kernel: this.options.kernel, threads: this.options.threads });
2172
+ world.setTilt(this.tilt);
2173
+ world.gpuMinTiles = this.gpuMinTiles;
2174
+ if (this.gpu) world.attachGpu(this.gpu);
2175
+ else this.startGpu();
2176
+ return world;
2177
+ }
2178
+ /**
2179
+ * Whether `extend` can keep the wet paint when the canvas grows by `left` / `top` on those
2180
+ * sides: the shift has to be whole wet tiles (256 px), and no wet stroke may be live.
2181
+ */
2182
+ canExtend(left, top) {
2183
+ return left % WET_TILE === 0 && top % WET_TILE === 0 && !this.driver;
2184
+ }
2185
+ /**
2186
+ * Keeps the wet paint while the canvas grows to `width`×`height` by `left` / `top` on those
2187
+ * sides (`canExtend` must hold): the world is made again at the new size with every wet layer
2188
+ * moved by (left, top), still wet, its base and flow map with it; `paper` is the new
2189
+ * document's paper (its grain moved with the paint). Wet settings' flow centres move too. The
2190
+ * caller shows the layers again (`syncDisplays`) once the document is the new size.
2191
+ */
2192
+ extend(width, height, left, top, paper) {
2193
+ if (!this.canExtend(left, top)) throw new Error("Wet paint only moves by whole 256 px tiles, between strokes.");
2194
+ for (const [id, s] of this.settings) if (s.flow.center) this.settings.set(id, { ...s, flow: { ...s.flow, center: { x: s.flow.center.x + left, y: s.flow.center.y + top } } });
2195
+ const old = this.world;
2196
+ const snap = old?.laneIds().length ? old.snapshot() : null;
2197
+ this.dropWorld();
2198
+ this.lastTick = null;
2199
+ this.stepsOwed = 0;
2200
+ if (!old || !snap) return;
2201
+ const moved = moveWetSnapshot(snap, old, { width, height }, left, top, (l) => {
2202
+ const flow = this.settings.get(l.id)?.flow;
2203
+ return {
2204
+ base: translateSurface(l.base, left, top),
2205
+ flow: l.flow && flow && flow.kind !== "none" ? flowField(flow, width, height) : l.flow
2206
+ };
2207
+ });
2208
+ this.world = this.makeWorld(width, height, paper);
2209
+ this.world.restore(moved);
2210
+ }
2174
2211
  /** Whether any layer (or `layerId`) has wet paint. */
2175
2212
  isWet(layerId) {
2176
2213
  const w = this.world;
@@ -3709,9 +3746,11 @@ var PaintEditorController = class _PaintEditorController {
3709
3746
  * move by (left, top) in document pixels, `document.origin` moves by (−left, −top), and the view
3710
3747
  * pans so the picture stays still on screen.
3711
3748
  *
3712
- * Wet paint dries first (the wet simulation is canvas-sized), as with `setCanvas`; one undo step
3713
- * (undo brings back the old size, origin and guides, and the wet paint). Returns the new canvas,
3714
- * where it sits in the previous document's pixels, and the origin.
3749
+ * Wet paint stays wet and moves with the picture when `left` and `top` are whole 256 px tiles
3750
+ * (so a host growing a canvas while someone paints should grow it in tile steps); otherwise it
3751
+ * dries first (the wet simulation is canvas-sized), as with `setCanvas`. One undo step (undo
3752
+ * brings back the old size, origin and guides, and the wet paint). Returns the new canvas, where
3753
+ * it sits in the previous document's pixels, and the origin.
3715
3754
  */
3716
3755
  extendCanvas(by) {
3717
3756
  this.requireIdle("extending the canvas");
@@ -3731,14 +3770,29 @@ var PaintEditorController = class _PaintEditorController {
3731
3770
  if (width > MAX_CANVAS_SIDE || height > MAX_CANVAS_SIDE) {
3732
3771
  throw new PaintEditorError(`The canvas would be ${width}\xD7${height}; the limit is ${MAX_CANVAS_SIDE} px per side. Extend by less (it is ${was.width}\xD7${was.height} now).`);
3733
3772
  }
3773
+ let keptWet = [];
3734
3774
  if (this.wet.world) {
3735
3775
  this.requireWetIdle("extending the canvas");
3736
- this.dryLayersFirst(this.wet.wetLayerIds());
3737
- const settings = [...this.wet.settings];
3738
- this.wet.reset();
3739
- for (const [id, s] of settings) this.wet.settings.set(id, s.flow.center ? { ...s, flow: { ...s.flow, center: { x: s.flow.center.x + left, y: s.flow.center.y + top } } } : s);
3776
+ if (this.wet.canExtend(left, top)) {
3777
+ keptWet = this.wet.wetLayerIds();
3778
+ if (keptWet.length) this.beginWetOp();
3779
+ } else {
3780
+ this.dryLayersFirst(this.wet.wetLayerIds());
3781
+ }
3782
+ }
3783
+ let next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
3784
+ for (const id of keptWet) {
3785
+ const l = findLayer(next.layers, id)?.layer;
3786
+ if (isPixelLayer(l)) next = { ...next, layers: mapLayer(next.layers, id, () => ({ ...l, surface: translateSurface(l.surface, l.offset.x, l.offset.y), offset: { x: 0, y: 0 } })) };
3787
+ }
3788
+ if (this.wet.world) {
3789
+ if (keptWet.length) this.wet.extend(width, height, left, top, next.paper);
3790
+ else {
3791
+ const settings = [...this.wet.settings];
3792
+ this.wet.reset();
3793
+ for (const [id, s] of settings) this.wet.settings.set(id, s.flow.center ? { ...s, flow: { ...s.flow, center: { x: s.flow.center.x + left, y: s.flow.center.y + top } } } : s);
3794
+ }
3740
3795
  }
3741
- const next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
3742
3796
  const m = viewMatrix(this.view, was, { width: 0, height: 0 });
3743
3797
  const dx = (right - left) / 2;
3744
3798
  const dy = (bottom - top) / 2;
@@ -3746,6 +3800,7 @@ var PaintEditorController = class _PaintEditorController {
3746
3800
  const guidesBefore = this.guides;
3747
3801
  if (left || top) this.restoreGuides(shiftGuides(this.guides, left, top));
3748
3802
  this.commit("Extend canvas", next);
3803
+ if (keptWet.length) this.wet.syncDisplays();
3749
3804
  if (this.guides !== guidesBefore) this.undoStack[this.undoStack.length - 1].guides = { before: guidesBefore, after: this.guides };
3750
3805
  const origin = { ...next.origin ?? { x: 0, y: 0 } };
3751
3806
  return { canvas: { ...next.canvas }, rect: { x: 0 - left, y: 0 - top, width, height }, origin };
@@ -7433,7 +7488,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
7433
7488
  add({
7434
7489
  name: "extend_canvas",
7435
7490
  title: "Extend the canvas",
7436
- description: "Grows the canvas by adding pixels on any side, keeping everything painted. Adding on the left or top moves the whole picture (layers, paper grain, guides, stencils, recorded strokes) right or down by that much in document pixels, so coordinates you read before change by (+left, +top). Wet paint dries first. One undo step. Returns the canvas, rect (the new canvas in the previous coordinates: x -left, y -top) and origin.",
7491
+ description: "Grows the canvas by adding pixels on any side, keeping everything painted. Adding on the left or top moves the whole picture (layers, paper grain, guides, stencils, recorded strokes) right or down by that much in document pixels, so coordinates you read before change by (+left, +top). Wet paint stays wet and moves with it when left and top are multiples of 256; otherwise it dries first. One undo step. Returns the canvas, rect (the new canvas in the previous coordinates: x -left, y -top) and origin.",
7437
7492
  inputSchema: {
7438
7493
  type: "object",
7439
7494
  properties: { left: EXTEND_SIDE("left"), top: EXTEND_SIDE("top"), right: EXTEND_SIDE("right"), bottom: EXTEND_SIDE("bottom") },
@@ -214,6 +214,20 @@ export declare class PaintWetLayers {
214
214
  engineInfo(): WetEngineInfo;
215
215
  /** The world (made on first use, at the canvas size). */
216
216
  ensureWorld(): WetWorld;
217
+ private makeWorld;
218
+ /**
219
+ * Whether `extend` can keep the wet paint when the canvas grows by `left` / `top` on those
220
+ * sides: the shift has to be whole wet tiles (256 px), and no wet stroke may be live.
221
+ */
222
+ canExtend(left: number, top: number): boolean;
223
+ /**
224
+ * Keeps the wet paint while the canvas grows to `width`×`height` by `left` / `top` on those
225
+ * sides (`canExtend` must hold): the world is made again at the new size with every wet layer
226
+ * moved by (left, top), still wet, its base and flow map with it; `paper` is the new
227
+ * document's paper (its grain moved with the paint). Wet settings' flow centres move too. The
228
+ * caller shows the layers again (`syncDisplays`) once the document is the new size.
229
+ */
230
+ extend(width: number, height: number, left: number, top: number, paper: PaperSettings): void;
217
231
  /** Whether any layer (or `layerId`) has wet paint. */
218
232
  isWet(layerId?: string): boolean;
219
233
  /** Layers holding a wet layer (wet or drying). */
@@ -214,6 +214,20 @@ export declare class PaintWetLayers {
214
214
  engineInfo(): WetEngineInfo;
215
215
  /** The world (made on first use, at the canvas size). */
216
216
  ensureWorld(): WetWorld;
217
+ private makeWorld;
218
+ /**
219
+ * Whether `extend` can keep the wet paint when the canvas grows by `left` / `top` on those
220
+ * sides: the shift has to be whole wet tiles (256 px), and no wet stroke may be live.
221
+ */
222
+ canExtend(left: number, top: number): boolean;
223
+ /**
224
+ * Keeps the wet paint while the canvas grows to `width`×`height` by `left` / `top` on those
225
+ * sides (`canExtend` must hold): the world is made again at the new size with every wet layer
226
+ * moved by (left, top), still wet, its base and flow map with it; `paper` is the new
227
+ * document's paper (its grain moved with the paint). Wet settings' flow centres move too. The
228
+ * caller shows the layers again (`syncDisplays`) once the document is the new size.
229
+ */
230
+ extend(width: number, height: number, left: number, top: number, paper: PaperSettings): void;
217
231
  /** Whether any layer (or `layerId`) has wet paint. */
218
232
  isWet(layerId?: string): boolean;
219
233
  /** Layers holding a wet layer (wet or drying). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.217.0",
3
+ "version": "0.218.1",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -283,7 +283,7 @@ view), so work with the tools you are given.
283
283
  | `get_canvas_info` | Read the canvas size and dpi, its `origin` (moved by `extend_canvas`), paper (preset, colour, depth), active layer, layer count, active brush id, colours, view, hand, number of recorded strokes, undo / redo labels and the blend modes. Call first. |
284
284
  | `new_canvas` | Start over with a blank canvas: one empty layer over the paper. `width`, `height` (1..16384 px), `dpi`, `paper` preset id; missing ones keep the current values. Undo brings the old painting back. Returns the size, paper and layer id. |
285
285
  | `set_canvas` | Resize the canvas (pixels stay put, anchored top-left) or change its `dpi`. Returns the canvas. |
286
- | `extend_canvas` | Add pixels on any side (`left`, `top`, `right`, `bottom`, default 0) keeping everything painted, the paper grain and the guides in place relative to each other. `left` / `top` move all document coordinates by that much. Wet paint dries first; one undo step. Returns `canvas`, `rect` (the new canvas in the previous coordinates) and `origin` (also in `get_canvas_info`). |
286
+ | `extend_canvas` | Add pixels on any side (`left`, `top`, `right`, `bottom`, default 0) keeping everything painted, the paper grain and the guides in place relative to each other. `left` / `top` move all document coordinates by that much. Wet paint stays wet and moves with it when `left` and `top` are multiples of 256; otherwise it dries first. One undo step. Returns `canvas`, `rect` (the new canvas in the previous coordinates) and `origin` (also in `get_canvas_info`). |
287
287
  | `list_papers` | Read the paper library: id, name, family, grain pattern, colour, depth and the wet-media properties (absorption, permeability, roughness, stored for later wet media). Call before `set_paper`. |
288
288
  | `get_paper` | Read every setting of the current paper (pattern, seed, scale, tiling, depth, shading, dry and wet tuning). |
289
289
  | `set_paper` | Change the paper: `preset`, then any of `color`, `depth` (0..1), `scale` (0.25..8), `seed` (another sheet), `pattern`, `tiling`, `shading`, `dry` / `wet` tuning (`contrast` 0..3, `invert`). Affects new strokes' grain and how the paper shows; strokes already painted keep their pixels. Returns the paper. |