@pixerate/editor 0.155.0 → 0.156.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.
- package/dist/animation-editor/index.cjs +58 -58
- package/dist/animation-editor/index.js +4 -4
- package/dist/{brushstrokeRender-YAZVQUXY.cjs → brushstrokeRender-5WANQIG4.cjs} +10 -10
- package/dist/{brushstrokeRender-EUDQTDYS.js → brushstrokeRender-Q4LI22QV.js} +3 -3
- package/dist/canvas/index.cjs +8 -8
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-AGZ47XY2.js → chunk-2N2QPO3H.js} +6 -2
- package/dist/{chunk-NMXPOKF3.js → chunk-45XXVHVG.js} +1 -1
- package/dist/{chunk-FLOGD3QB.js → chunk-6UPGM3JB.js} +2 -2
- package/dist/{chunk-6FUAHCVI.cjs → chunk-7NW46N7I.cjs} +16 -16
- package/dist/{chunk-ORDXMBD4.js → chunk-A5UCR2TO.js} +10 -3
- package/dist/{chunk-PI45JMP7.cjs → chunk-CD7PLU77.cjs} +81 -81
- package/dist/{chunk-LRRCMRSL.cjs → chunk-EBDCRTZI.cjs} +20 -16
- package/dist/{chunk-OLCYQLFE.js → chunk-EL5PAHNP.js} +1 -1
- package/dist/{chunk-PUC43JPT.cjs → chunk-EYVVIZJ3.cjs} +1 -1
- package/dist/{chunk-BXMIBMVG.cjs → chunk-JGKCC5OF.cjs} +65 -58
- package/dist/{chunk-LIXLD433.cjs → chunk-MHEXLG3M.cjs} +35 -35
- package/dist/{chunk-RHXW4TA5.cjs → chunk-QMI5HZWA.cjs} +7 -7
- package/dist/{chunk-UILRJQFD.js → chunk-WNAAWLZ5.js} +1 -1
- package/dist/{chunk-Y64YPIFT.js → chunk-ZQWLWDZI.js} +1 -1
- package/dist/image-editor/index.cjs +686 -686
- package/dist/image-editor/index.js +5 -5
- package/dist/paint/grain.d.cts +7 -0
- package/dist/paint/grain.d.ts +7 -0
- package/dist/paint/index.cjs +3 -3
- package/dist/paint/index.js +2 -2
- package/dist/paint-editor/controller.d.cts +30 -0
- package/dist/paint-editor/controller.d.ts +30 -0
- package/dist/paint-editor/document.d.cts +8 -0
- package/dist/paint-editor/document.d.ts +8 -0
- package/dist/paint-editor/index.cjs +403 -256
- package/dist/paint-editor/index.js +158 -11
- package/dist/paint-editor/paper.d.cts +8 -0
- package/dist/paint-editor/paper.d.ts +8 -0
- package/dist/paint-editor/stencils.d.cts +2 -0
- package/dist/paint-editor/stencils.d.ts +2 -0
- package/dist/paint-editor/wet.d.cts +1 -0
- package/dist/paint-editor/wet.d.ts +1 -0
- package/dist/vector/index.cjs +950 -950
- package/dist/vector/index.js +4 -4
- package/package.json +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +9 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
resolveExternalAssets,
|
|
8
8
|
serializeLayers,
|
|
9
9
|
serializeLayersExternal
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-ZQWLWDZI.js";
|
|
11
11
|
import "../chunk-3CI7FRMK.js";
|
|
12
12
|
import {
|
|
13
13
|
EffectRendererPool
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
realPresenceClock,
|
|
55
55
|
tweenPresenceCursor
|
|
56
56
|
} from "../chunk-O2ANJFUF.js";
|
|
57
|
-
import "../chunk-
|
|
57
|
+
import "../chunk-6UPGM3JB.js";
|
|
58
58
|
import {
|
|
59
59
|
BRUSH_ID,
|
|
60
60
|
BRUSH_ROTATIONS,
|
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
tiltToOrientation,
|
|
120
120
|
toStrokePoint,
|
|
121
121
|
usesPaintExchange
|
|
122
|
-
} from "../chunk-
|
|
122
|
+
} from "../chunk-A5UCR2TO.js";
|
|
123
123
|
import "../chunk-M6DNZNVY.js";
|
|
124
124
|
import "../chunk-WEIK3UYB.js";
|
|
125
125
|
import "../chunk-PWBZH3FK.js";
|
|
@@ -189,7 +189,7 @@ import {
|
|
|
189
189
|
seedFromString,
|
|
190
190
|
solidPaint,
|
|
191
191
|
updatePaintGradientStop
|
|
192
|
-
} from "../chunk-
|
|
192
|
+
} from "../chunk-2N2QPO3H.js";
|
|
193
193
|
import "../chunk-ODBT55Q7.js";
|
|
194
194
|
import {
|
|
195
195
|
BLEND_MODES,
|
|
@@ -1357,6 +1357,17 @@ function stencilMask(stencils, width, height) {
|
|
|
1357
1357
|
}
|
|
1358
1358
|
return { width, height, data };
|
|
1359
1359
|
}
|
|
1360
|
+
function translateStencil(s, dx, dy) {
|
|
1361
|
+
const p = (q) => q ? { x: q.x + dx, y: q.y + dy } : void 0;
|
|
1362
|
+
return {
|
|
1363
|
+
...s,
|
|
1364
|
+
...s.from ? { from: p(s.from) } : {},
|
|
1365
|
+
...s.to ? { to: p(s.to) } : {},
|
|
1366
|
+
...s.center ? { center: p(s.center) } : {},
|
|
1367
|
+
...s.rect ? { rect: { ...s.rect, x: s.rect.x + dx, y: s.rect.y + dy } } : {},
|
|
1368
|
+
...s.points ? { points: s.points.map((q) => p(q)) } : {}
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1360
1371
|
function scaleStencil(s, k) {
|
|
1361
1372
|
const p = (q) => q ? { x: q.x * k, y: q.y * k } : void 0;
|
|
1362
1373
|
return {
|
|
@@ -1507,7 +1518,7 @@ var TRACING_NODE_ID = "__tracing";
|
|
|
1507
1518
|
var PAPER_EFFECT_NODE_ID = "__paper_effect";
|
|
1508
1519
|
function paperKey(p, light) {
|
|
1509
1520
|
const metal = p.metallic ?? 0;
|
|
1510
|
-
return JSON.stringify([p.pattern, p.seed, p.color, p.scale, p.tiling, p.shading, p.dry, metal, metal > 0 && light ? [light.azimuth, light.elevation] : null]);
|
|
1521
|
+
return JSON.stringify([p.pattern, p.seed, p.color, p.scale, p.tiling, p.shading, p.dry, p.offset ?? null, metal, metal > 0 && light ? [light.azimuth, light.elevation] : null]);
|
|
1511
1522
|
}
|
|
1512
1523
|
var paperNodes = /* @__PURE__ */ new WeakMap();
|
|
1513
1524
|
function paperNode(paper, light) {
|
|
@@ -1522,7 +1533,7 @@ function paperNode(paper, light) {
|
|
|
1522
1533
|
var grainCache = /* @__PURE__ */ new Map();
|
|
1523
1534
|
var tileCache = /* @__PURE__ */ new Map();
|
|
1524
1535
|
function paperGrain(paper) {
|
|
1525
|
-
const key = JSON.stringify([paper.pattern, paper.seed, paper.scale, paper.tiling, paper.dry]);
|
|
1536
|
+
const key = JSON.stringify([paper.pattern, paper.seed, paper.scale, paper.tiling, paper.dry, paper.offset ?? null]);
|
|
1526
1537
|
let g = grainCache.get(key);
|
|
1527
1538
|
if (!g) {
|
|
1528
1539
|
g = new PaperGrain(paper, "dry");
|
|
@@ -1612,7 +1623,7 @@ function downsampleRgba(data, width, height, tw, th) {
|
|
|
1612
1623
|
// src/paint-editor/serialize.ts
|
|
1613
1624
|
var PAINT_FORMAT = "pixerate-paint";
|
|
1614
1625
|
var PAINT_SCHEMA_VERSION = 1;
|
|
1615
|
-
var KNOWN = /* @__PURE__ */ new Set(["format", "version", "width", "height", "dpi", "paper", "layers", "assets", "activeLayerId", "strokes", "guides", "swatches", "mixingPad", "wet", "wetEvents", "paintEvents"]);
|
|
1626
|
+
var KNOWN = /* @__PURE__ */ new Set(["format", "version", "width", "height", "dpi", "paper", "layers", "assets", "activeLayerId", "strokes", "guides", "swatches", "mixingPad", "wet", "wetEvents", "paintEvents", "origin"]);
|
|
1616
1627
|
function documentExtensions(raw) {
|
|
1617
1628
|
const out = {};
|
|
1618
1629
|
for (const [k, v] of Object.entries(raw)) if (!KNOWN.has(k)) out[k] = v;
|
|
@@ -1636,6 +1647,7 @@ function header(state, options) {
|
|
|
1636
1647
|
if (options.wet !== void 0) out.wet = JSON.parse(JSON.stringify(options.wet));
|
|
1637
1648
|
if (state.wetEvents?.length) out.wetEvents = JSON.parse(JSON.stringify(state.wetEvents));
|
|
1638
1649
|
if (state.paintEvents?.length) out.paintEvents = JSON.parse(JSON.stringify(state.paintEvents));
|
|
1650
|
+
if (state.origin && (state.origin.x || state.origin.y)) out.origin = { ...state.origin };
|
|
1639
1651
|
return out;
|
|
1640
1652
|
}
|
|
1641
1653
|
function serializePaintDocument(state, options = {}) {
|
|
@@ -1688,6 +1700,8 @@ function deserializePaintDocument(raw) {
|
|
|
1688
1700
|
if (wetEvents.length) state.wetEvents = wetEvents;
|
|
1689
1701
|
const paintEvents = Array.isArray(r.paintEvents) ? r.paintEvents.filter((e) => e && typeof e === "object" && Number.isInteger(e.at) && typeof e.layerId === "string" && ["fill", "clear", "flatten", "stickers"].includes(e.kind)) : [];
|
|
1690
1702
|
if (paintEvents.length) state.paintEvents = paintEvents;
|
|
1703
|
+
const o = r.origin;
|
|
1704
|
+
if (o && typeof o === "object" && Number.isFinite(o.x) && Number.isFinite(o.y) && (o.x || o.y)) state.origin = { x: Math.round(o.x), y: Math.round(o.y) };
|
|
1691
1705
|
return { state, extensions: documentExtensions(r), guides: r.guides, swatches: r.swatches, mixingPad: r.mixingPad, wet: r.wet, problems };
|
|
1692
1706
|
}
|
|
1693
1707
|
async function deserializePaintDocumentAsync(raw, store) {
|
|
@@ -2138,7 +2152,10 @@ var PaintWetLayers = class {
|
|
|
2138
2152
|
/** The world (made on first use, at the canvas size). */
|
|
2139
2153
|
ensureWorld() {
|
|
2140
2154
|
const { width, height } = this.host.canvas();
|
|
2141
|
-
if (this.world && (this.world.width !== width || this.world.height !== height))
|
|
2155
|
+
if (this.world && (this.world.width !== width || this.world.height !== height)) {
|
|
2156
|
+
if (this.world.laneIds().length) throw new Error("The wet world is the wrong size; dry the paint before resizing the canvas.");
|
|
2157
|
+
this.dropWorld();
|
|
2158
|
+
}
|
|
2142
2159
|
if (!this.world) {
|
|
2143
2160
|
this.world = new WetWorld(width, height, wetPaperInput(this.host.paper()), { kernel: this.options.kernel, threads: this.options.threads });
|
|
2144
2161
|
this.world.setTilt(this.tilt);
|
|
@@ -2401,9 +2418,16 @@ var PaintWetLayers = class {
|
|
|
2401
2418
|
if (this.world) for (const id of this.world.laneIds()) this.world.removeLane(id);
|
|
2402
2419
|
return;
|
|
2403
2420
|
}
|
|
2421
|
+
const { width, height } = this.host.canvas();
|
|
2422
|
+
if (this.world && (this.world.width !== width || this.world.height !== height)) this.dropWorld();
|
|
2404
2423
|
this.ensureWorld().restore(snap);
|
|
2405
2424
|
this.syncDisplays();
|
|
2406
2425
|
}
|
|
2426
|
+
dropWorld() {
|
|
2427
|
+
this.world?.destroy();
|
|
2428
|
+
this.world = null;
|
|
2429
|
+
this.driver = null;
|
|
2430
|
+
}
|
|
2407
2431
|
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
2408
2432
|
reset() {
|
|
2409
2433
|
this.world?.destroy();
|
|
@@ -3594,6 +3618,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3594
3618
|
const paper = this.doc.paper;
|
|
3595
3619
|
this.doc = e.before;
|
|
3596
3620
|
this.docRevision++;
|
|
3621
|
+
if (e.guides) this.restoreGuides(e.guides.before);
|
|
3597
3622
|
if (e.wet) {
|
|
3598
3623
|
if (paper !== this.doc.paper) this.wet.paperChanged();
|
|
3599
3624
|
this.wet.restore(e.wet.lost ? null : e.wet.before);
|
|
@@ -3612,6 +3637,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3612
3637
|
const paper = this.doc.paper;
|
|
3613
3638
|
this.doc = e.after;
|
|
3614
3639
|
this.docRevision++;
|
|
3640
|
+
if (e.guides) this.restoreGuides(e.guides.after);
|
|
3615
3641
|
if (e.wet) {
|
|
3616
3642
|
this.wet.restore(e.wet.lost ? null : e.wet.after);
|
|
3617
3643
|
if (paper !== this.doc.paper) this.wet.paperChanged();
|
|
@@ -3619,6 +3645,10 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3619
3645
|
this.emit();
|
|
3620
3646
|
return e.label;
|
|
3621
3647
|
}
|
|
3648
|
+
restoreGuides(guides) {
|
|
3649
|
+
if (guides.tracing !== this.guides.tracing) this.tracingVersion++;
|
|
3650
|
+
this.guides = guides;
|
|
3651
|
+
}
|
|
3622
3652
|
/** Labels of the undo steps, oldest first. */
|
|
3623
3653
|
historyLabels() {
|
|
3624
3654
|
return { undo: this.undoStack.map((e) => e.label), redo: this.redoStack.map((e) => e.label).reverse() };
|
|
@@ -3639,7 +3669,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3639
3669
|
width: options.width ?? this.doc.canvas.width,
|
|
3640
3670
|
height: options.height ?? this.doc.canvas.height,
|
|
3641
3671
|
dpi: options.dpi ?? this.doc.canvas.dpi,
|
|
3642
|
-
paper: options.paper === void 0 ? this.doc.paper : this.resolvePaper(options.paper)
|
|
3672
|
+
paper: options.paper === void 0 ? { ...this.doc.paper, offset: void 0 } : this.resolvePaper(options.paper)
|
|
3643
3673
|
});
|
|
3644
3674
|
this.extensions = {};
|
|
3645
3675
|
this.guides = { ...this.guides, symmetry: { ...this.guides.symmetry, center: { x: next.canvas.width / 2, y: next.canvas.height / 2 } }, tracing: null };
|
|
@@ -3666,6 +3696,54 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3666
3696
|
this.commit("Canvas size", this.withWetEvents({ ...this.doc, canvas }));
|
|
3667
3697
|
return canvas;
|
|
3668
3698
|
}
|
|
3699
|
+
/**
|
|
3700
|
+
* Grows the canvas by `left`, `top`, `right` and `bottom` pixels without moving anything
|
|
3701
|
+
* relative to anything else: the layers (with their masks, relief and stickers), the paper grain,
|
|
3702
|
+
* the effect paper, guides, stencils, the tracing image and the recorded strokes and events all
|
|
3703
|
+
* move by (left, top) in document pixels, `document.origin` moves by (−left, −top), and the view
|
|
3704
|
+
* pans so the picture stays still on screen.
|
|
3705
|
+
*
|
|
3706
|
+
* Wet paint dries first (the wet simulation is canvas-sized), as with `setCanvas`; one undo step
|
|
3707
|
+
* (undo brings back the old size, origin and guides, and the wet paint). Returns the new canvas,
|
|
3708
|
+
* where it sits in the previous document's pixels, and the origin.
|
|
3709
|
+
*/
|
|
3710
|
+
extendCanvas(by) {
|
|
3711
|
+
this.requireIdle("extending the canvas");
|
|
3712
|
+
const side = (k) => {
|
|
3713
|
+
const v = by[k] ?? 0;
|
|
3714
|
+
if (!Number.isInteger(v) || v < 0) throw new PaintEditorError(`${k} is ${JSON.stringify(by[k])}; use a whole number of pixels to add, 0 or more.`);
|
|
3715
|
+
return v;
|
|
3716
|
+
};
|
|
3717
|
+
const left = side("left");
|
|
3718
|
+
const top = side("top");
|
|
3719
|
+
const right = side("right");
|
|
3720
|
+
const bottom = side("bottom");
|
|
3721
|
+
if (!(left || top || right || bottom)) throw new PaintEditorError("Nothing to extend: give left, top, right or bottom (pixels to add on that side) above 0.");
|
|
3722
|
+
const was = this.doc.canvas;
|
|
3723
|
+
const width = was.width + left + right;
|
|
3724
|
+
const height = was.height + top + bottom;
|
|
3725
|
+
if (width > MAX_CANVAS_SIDE || height > MAX_CANVAS_SIDE) {
|
|
3726
|
+
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).`);
|
|
3727
|
+
}
|
|
3728
|
+
if (this.wet.world) {
|
|
3729
|
+
this.requireWetIdle("extending the canvas");
|
|
3730
|
+
this.dryLayersFirst(this.wet.wetLayerIds());
|
|
3731
|
+
const settings = [...this.wet.settings];
|
|
3732
|
+
this.wet.reset();
|
|
3733
|
+
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);
|
|
3734
|
+
}
|
|
3735
|
+
const next = { ...shiftDocument(this.withWetEvents(this.doc), left, top), canvas: { ...was, width, height } };
|
|
3736
|
+
const m = viewMatrix(this.view, was, { width: 0, height: 0 });
|
|
3737
|
+
const dx = (right - left) / 2;
|
|
3738
|
+
const dy = (bottom - top) / 2;
|
|
3739
|
+
this.view = { ...this.view, pan: { x: this.view.pan.x + m[0] * dx + m[1] * dy, y: this.view.pan.y + m[2] * dx + m[3] * dy } };
|
|
3740
|
+
const guidesBefore = this.guides;
|
|
3741
|
+
if (left || top) this.restoreGuides(shiftGuides(this.guides, left, top));
|
|
3742
|
+
this.commit("Extend canvas", next);
|
|
3743
|
+
if (this.guides !== guidesBefore) this.undoStack[this.undoStack.length - 1].guides = { before: guidesBefore, after: this.guides };
|
|
3744
|
+
const origin = { ...next.origin ?? { x: 0, y: 0 } };
|
|
3745
|
+
return { canvas: { ...next.canvas }, rect: { x: 0 - left, y: 0 - top, width, height }, origin };
|
|
3746
|
+
}
|
|
3669
3747
|
/** Changes the paper: a library preset (`preset`), and / or any of its settings (one undo step). */
|
|
3670
3748
|
setPaper(update) {
|
|
3671
3749
|
this.requireIdle("changing the paper");
|
|
@@ -3677,7 +3755,7 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
3677
3755
|
}
|
|
3678
3756
|
const { preset: _p, ...rest } = update;
|
|
3679
3757
|
if (rest.color !== void 0) parseColor(rest.color, "paper color");
|
|
3680
|
-
const paper = normalizePaper({ ...rest, preset: base.preset }, base);
|
|
3758
|
+
const paper = normalizePaper({ ...rest, preset: base.preset, offset: rest.offset ?? this.doc.paper.offset }, base);
|
|
3681
3759
|
const changedKeys = Object.keys(rest).filter((k) => k !== "name");
|
|
3682
3760
|
if (changedKeys.length && update.preset === void 0) paper.preset = base.preset;
|
|
3683
3761
|
if (this.wet.world) this.beginWetOp();
|
|
@@ -5839,6 +5917,60 @@ var PaintEditorController = class _PaintEditorController {
|
|
|
5839
5917
|
}
|
|
5840
5918
|
};
|
|
5841
5919
|
var MAX_IMAGE_SIDE = 8192;
|
|
5920
|
+
var movePoint = (p, dx, dy) => ({ x: p.x + dx, y: p.y + dy });
|
|
5921
|
+
var moveRect = (r, dx, dy) => ({ ...r, x: r.x + dx, y: r.y + dy });
|
|
5922
|
+
function shiftDocument(doc, dx, dy) {
|
|
5923
|
+
if (!dx && !dy) return doc;
|
|
5924
|
+
let layers = doc.layers;
|
|
5925
|
+
for (const l of flattenLayers(doc.layers)) if (l.type === "pixel") layers = mapLayer(layers, l.id, () => ({ ...l, offset: movePoint(l.offset, dx, dy) }));
|
|
5926
|
+
const m = [1, 0, 0, 1, dx, dy];
|
|
5927
|
+
const moveStickers = (list) => list.map((s) => ({ ...s, x: Math.round((s.x + dx) * 100) / 100, y: Math.round((s.y + dy) * 100) / 100 }));
|
|
5928
|
+
const out = {
|
|
5929
|
+
...doc,
|
|
5930
|
+
layers,
|
|
5931
|
+
paper: { ...doc.paper, offset: movePoint(doc.paper.offset ?? { x: 0, y: 0 }, dx, dy) },
|
|
5932
|
+
origin: movePoint(doc.origin ?? { x: 0, y: 0 }, -dx, -dy),
|
|
5933
|
+
strokes: doc.strokes.map((s) => {
|
|
5934
|
+
const moved = varyStroke(s, m, {});
|
|
5935
|
+
return s.stencils ? { ...moved, stencils: s.stencils.map((x) => translateStencil(x, dx, dy)) } : moved;
|
|
5936
|
+
})
|
|
5937
|
+
};
|
|
5938
|
+
if (doc.stickers) out.stickers = Object.fromEntries(Object.entries(doc.stickers).map(([id, list]) => [id, moveStickers(list)]));
|
|
5939
|
+
if (doc.paintEvents) {
|
|
5940
|
+
out.paintEvents = doc.paintEvents.map((e) => {
|
|
5941
|
+
const list = e.kind === "stickers" ? e.data?.stickers : void 0;
|
|
5942
|
+
return list ? { ...e, data: { ...e.data, stickers: moveStickers(list) } } : e;
|
|
5943
|
+
});
|
|
5944
|
+
}
|
|
5945
|
+
if (doc.wetEvents) {
|
|
5946
|
+
out.wetEvents = doc.wetEvents.map((e) => {
|
|
5947
|
+
const d = e.data;
|
|
5948
|
+
if (!d) return e;
|
|
5949
|
+
if (e.kind === "dry" && typeof d.x === "number") return { ...e, data: moveRect(d, dx, dy) };
|
|
5950
|
+
if (e.kind === "wet-paper" && d.rect) return { ...e, data: { ...d, rect: moveRect(d.rect, dx, dy) } };
|
|
5951
|
+
if (e.kind === "flow" && d.center) return { ...e, data: { ...d, center: movePoint(d.center, dx, dy) } };
|
|
5952
|
+
return e;
|
|
5953
|
+
});
|
|
5954
|
+
}
|
|
5955
|
+
if (doc.paperEffect) {
|
|
5956
|
+
const src = doc.paperEffect.image;
|
|
5957
|
+
const width = src.width + dx;
|
|
5958
|
+
const height = src.height + dy;
|
|
5959
|
+
const data = new Uint8ClampedArray(width * height * 4);
|
|
5960
|
+
for (let y = 0; y < src.height; y++) data.set(src.data.subarray(y * src.width * 4, (y + 1) * src.width * 4), ((y + dy) * width + dx) * 4);
|
|
5961
|
+
out.paperEffect = { ...doc.paperEffect, image: { width, height, data }, version: nextPaperEffectVersion() };
|
|
5962
|
+
}
|
|
5963
|
+
return out;
|
|
5964
|
+
}
|
|
5965
|
+
function shiftGuides(g, dx, dy) {
|
|
5966
|
+
return {
|
|
5967
|
+
...g,
|
|
5968
|
+
symmetry: { ...g.symmetry, center: movePoint(g.symmetry.center, dx, dy) },
|
|
5969
|
+
perspective: { ...g.perspective, points: g.perspective.points.map((p) => movePoint(p, dx, dy)) },
|
|
5970
|
+
tracing: g.tracing ? { ...g.tracing, rect: moveRect(g.tracing.rect, dx, dy) } : null,
|
|
5971
|
+
stencils: g.stencils.map((s) => translateStencil(s, dx, dy))
|
|
5972
|
+
};
|
|
5973
|
+
}
|
|
5842
5974
|
function resampleInto(src, rect, width, height) {
|
|
5843
5975
|
const out = new Uint8ClampedArray(width * height * 4);
|
|
5844
5976
|
const x0 = Math.max(0, Math.floor(rect.x));
|
|
@@ -7167,7 +7299,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7167
7299
|
add({
|
|
7168
7300
|
name: "get_canvas_info",
|
|
7169
7301
|
title: "Get the canvas",
|
|
7170
|
-
description: "Reads the canvas: size in pixels and dpi, the paper (preset, colour, grain depth), the active layer, the active brush id, the paint colour, the view (zoom, rotation, mirror), the hand, how many strokes are recorded, the undo / redo labels, and the blend modes. Call it first.",
|
|
7302
|
+
description: "Reads the canvas: size in pixels and dpi, its origin (where (0, 0) is relative to the canvas it was extended from), the paper (preset, colour, grain depth), the active layer, the active brush id, the paint colour, the view (zoom, rotation, mirror), the hand, how many strokes are recorded, the undo / redo labels, and the blend modes. Call it first.",
|
|
7171
7303
|
inputSchema: EMPTY3,
|
|
7172
7304
|
annotations: { title: "Get the canvas", ...READ6 },
|
|
7173
7305
|
handler: () => {
|
|
@@ -7177,6 +7309,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7177
7309
|
width: d.canvas.width,
|
|
7178
7310
|
height: d.canvas.height,
|
|
7179
7311
|
dpi: d.canvas.dpi,
|
|
7312
|
+
origin: { ...d.origin ?? { x: 0, y: 0 } },
|
|
7180
7313
|
paper: { preset: d.paper.preset, name: d.paper.name, color: d.paper.color, depth: d.paper.depth },
|
|
7181
7314
|
activeLayerId: d.activeLayerId,
|
|
7182
7315
|
layerCount: c.listLayers().length,
|
|
@@ -7230,6 +7363,19 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7230
7363
|
annotations: { title: "Resize the canvas", ...SET4 },
|
|
7231
7364
|
handler: (a) => c.setCanvas({ width: a.width, height: a.height, dpi: a.dpi })
|
|
7232
7365
|
});
|
|
7366
|
+
const EXTEND_SIDE = (side) => ({ type: "integer", minimum: 0, maximum: MAX_CANVAS_SIDE - 1, description: `Pixels to add on the ${side} (default 0).` });
|
|
7367
|
+
add({
|
|
7368
|
+
name: "extend_canvas",
|
|
7369
|
+
title: "Extend the canvas",
|
|
7370
|
+
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.",
|
|
7371
|
+
inputSchema: {
|
|
7372
|
+
type: "object",
|
|
7373
|
+
properties: { left: EXTEND_SIDE("left"), top: EXTEND_SIDE("top"), right: EXTEND_SIDE("right"), bottom: EXTEND_SIDE("bottom") },
|
|
7374
|
+
additionalProperties: false
|
|
7375
|
+
},
|
|
7376
|
+
annotations: { title: "Extend the canvas", ...WRITE5 },
|
|
7377
|
+
handler: (a) => c.extendCanvas({ left: a.left, top: a.top, right: a.right, bottom: a.bottom })
|
|
7378
|
+
});
|
|
7233
7379
|
add({
|
|
7234
7380
|
name: "list_papers",
|
|
7235
7381
|
title: "List papers",
|
|
@@ -8904,6 +9050,7 @@ export {
|
|
|
8904
9050
|
timeLapseZip,
|
|
8905
9051
|
toStrokePoint,
|
|
8906
9052
|
toggleStamp,
|
|
9053
|
+
translateStencil,
|
|
8907
9054
|
updatePaintGradientStop,
|
|
8908
9055
|
usesPaintExchange,
|
|
8909
9056
|
variationAffine,
|
|
@@ -40,6 +40,14 @@ export interface PaperSettings {
|
|
|
40
40
|
roughness: number;
|
|
41
41
|
/** Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light, 0..1 (default 0). */
|
|
42
42
|
metallic?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Where the grain's (0, 0) sits in document pixels (missing = the top-left). `extendCanvas` moves
|
|
45
|
+
* it with the paint, so the texture stays under what was painted on it.
|
|
46
|
+
*/
|
|
47
|
+
offset?: {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
};
|
|
43
51
|
}
|
|
44
52
|
type PaperPreset = PaperSettings;
|
|
45
53
|
/** The built-in paper library: generated papers in families. */
|
|
@@ -40,6 +40,14 @@ export interface PaperSettings {
|
|
|
40
40
|
roughness: number;
|
|
41
41
|
/** Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light, 0..1 (default 0). */
|
|
42
42
|
metallic?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Where the grain's (0, 0) sits in document pixels (missing = the top-left). `extendCanvas` moves
|
|
45
|
+
* it with the paint, so the texture stays under what was painted on it.
|
|
46
|
+
*/
|
|
47
|
+
offset?: {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
};
|
|
43
51
|
}
|
|
44
52
|
type PaperPreset = PaperSettings;
|
|
45
53
|
/** The built-in paper library: generated papers in families. */
|
|
@@ -76,5 +76,7 @@ export declare function stencilMask(stencils: readonly Stencil[], width: number,
|
|
|
76
76
|
height: number;
|
|
77
77
|
data: Uint8Array;
|
|
78
78
|
} | null;
|
|
79
|
+
/** A stencil moved by (dx, dy) document pixels (for a canvas extended to the left or top). */
|
|
80
|
+
export declare function translateStencil(s: Stencil, dx: number, dy: number): Stencil;
|
|
79
81
|
/** A stencil scaled about the origin by `k` (for repainting at a larger size). */
|
|
80
82
|
export declare function scaleStencil(s: Stencil, k: number): Stencil;
|
|
@@ -76,5 +76,7 @@ export declare function stencilMask(stencils: readonly Stencil[], width: number,
|
|
|
76
76
|
height: number;
|
|
77
77
|
data: Uint8Array;
|
|
78
78
|
} | null;
|
|
79
|
+
/** A stencil moved by (dx, dy) document pixels (for a canvas extended to the left or top). */
|
|
80
|
+
export declare function translateStencil(s: Stencil, dx: number, dy: number): Stencil;
|
|
79
81
|
/** A stencil scaled about the origin by `k` (for repainting at a larger size). */
|
|
80
82
|
export declare function scaleStencil(s: Stencil, k: number): Stencil;
|
|
@@ -266,6 +266,7 @@ export declare class PaintWetLayers {
|
|
|
266
266
|
snapshot(): WetSnapshot | null;
|
|
267
267
|
/** Puts a snapshot back (`null`: no wet layers). Returns the layer surfaces it implies. */
|
|
268
268
|
restore(snap: WetSnapshot | null): void;
|
|
269
|
+
private dropWorld;
|
|
269
270
|
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
270
271
|
reset(): void;
|
|
271
272
|
/** Water overlay tiles (RGBA) over a document rectangle, for the view. */
|
|
@@ -266,6 +266,7 @@ export declare class PaintWetLayers {
|
|
|
266
266
|
snapshot(): WetSnapshot | null;
|
|
267
267
|
/** Puts a snapshot back (`null`: no wet layers). Returns the layer surfaces it implies. */
|
|
268
268
|
restore(snap: WetSnapshot | null): void;
|
|
269
|
+
private dropWorld;
|
|
269
270
|
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
270
271
|
reset(): void;
|
|
271
272
|
/** Water overlay tiles (RGBA) over a document rectangle, for the view. */
|