@pixerate/editor 0.114.0 → 0.116.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/THIRD_PARTY_NOTICES.md +6 -0
- package/dist/animation-editor/index.cjs +54 -54
- package/dist/animation-editor/index.d.cts +2 -2
- package/dist/animation-editor/index.d.ts +2 -2
- package/dist/animation-editor/index.js +2 -2
- package/dist/{chunk-DFQEX3AM.cjs → chunk-2UT4GXYA.cjs} +133 -8
- package/dist/chunk-2UT4GXYA.cjs.map +1 -0
- package/dist/{chunk-VXR7XWMF.js → chunk-7LNZAWSJ.js} +437 -41
- package/dist/chunk-7LNZAWSJ.js.map +1 -0
- package/dist/{chunk-LKCYANMW.js → chunk-ARQ7PQ6Q.js} +129 -4
- package/dist/chunk-ARQ7PQ6Q.js.map +1 -0
- package/dist/chunk-COAMRKVI.cjs +159 -0
- package/dist/chunk-COAMRKVI.cjs.map +1 -0
- package/dist/{chunk-C6C5PDKU.js → chunk-D53KSJUV.js} +3 -322
- package/dist/chunk-D53KSJUV.js.map +1 -0
- package/dist/chunk-D53VQGEJ.js +3262 -0
- package/dist/chunk-D53VQGEJ.js.map +1 -0
- package/dist/chunk-EJVDBITD.cjs +501 -0
- package/dist/chunk-EJVDBITD.cjs.map +1 -0
- package/dist/{chunk-LPLCWZTM.cjs → chunk-FODKPD44.cjs} +555 -159
- package/dist/chunk-FODKPD44.cjs.map +1 -0
- package/dist/chunk-HOCPSFGA.cjs +3262 -0
- package/dist/chunk-HOCPSFGA.cjs.map +1 -0
- package/dist/chunk-IPFRWILU.js +1908 -0
- package/dist/chunk-IPFRWILU.js.map +1 -0
- package/dist/{chunk-FV5AXTSF.cjs → chunk-JSSBHMMU.cjs} +36 -188
- package/dist/chunk-JSSBHMMU.cjs.map +1 -0
- package/dist/chunk-LF4WOZXZ.js +159 -0
- package/dist/chunk-LF4WOZXZ.js.map +1 -0
- package/dist/{chunk-26PGXHY7.cjs → chunk-M7O4BJRH.cjs} +36 -36
- package/dist/{chunk-26PGXHY7.cjs.map → chunk-M7O4BJRH.cjs.map} +1 -1
- package/dist/chunk-N3467VD6.js +501 -0
- package/dist/chunk-N3467VD6.js.map +1 -0
- package/dist/{chunk-ST7E3NEE.cjs → chunk-PAOWHJ5O.cjs} +18 -337
- package/dist/chunk-PAOWHJ5O.cjs.map +1 -0
- package/dist/{chunk-EUVSQ25B.js → chunk-SEBYNBHW.js} +85 -46
- package/dist/chunk-SEBYNBHW.js.map +1 -0
- package/dist/chunk-SMJGE7HM.cjs +1908 -0
- package/dist/chunk-SMJGE7HM.cjs.map +1 -0
- package/dist/{chunk-MMVHBKMM.js → chunk-T2P5TIZ6.js} +2 -2
- package/dist/{chunk-FGW5OWJ4.js → chunk-VJUML33I.js} +4 -156
- package/dist/chunk-VJUML33I.js.map +1 -0
- package/dist/{chunk-5OVJHMZQ.cjs → chunk-ZYZGEILO.cjs} +332 -293
- package/dist/chunk-ZYZGEILO.cjs.map +1 -0
- package/dist/export/index.cjs +4 -2
- package/dist/export/index.cjs.map +1 -1
- package/dist/export/index.js +5 -3
- package/dist/{export-b6NjCZ5-.d.cts → export-BXxSGBCq.d.cts} +67 -5
- package/dist/{export-_1dUKwpF.d.ts → export-Dm44YUvS.d.ts} +67 -5
- package/dist/export-Dv0-ZVrV.d.cts +501 -0
- package/dist/export-Dv0-ZVrV.d.ts +501 -0
- package/dist/{retouch-Do6tBs2H.d.ts → flow-BMtHAmg4.d.ts} +561 -2
- package/dist/{retouch-Bxkuc0KH.d.cts → flow-COCB42TP.d.cts} +561 -2
- package/dist/image-editor/index.cjs +1227 -934
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +18 -10
- package/dist/image-editor/index.d.ts +18 -10
- package/dist/image-editor/index.js +324 -31
- package/dist/image-editor/index.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/kernel-wasm-WYCGNZR4.js +10 -0
- package/dist/kernel-wasm-WYCGNZR4.js.map +1 -0
- package/dist/kernel-wasm-YPVAEMVN.cjs +10 -0
- package/dist/kernel-wasm-YPVAEMVN.cjs.map +1 -0
- package/dist/paint/index.cjs +165 -3
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +225 -3
- package/dist/paint/index.d.ts +225 -3
- package/dist/paint/index.js +166 -4
- package/dist/paint-editor/index.cjs +4691 -845
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +1525 -277
- package/dist/paint-editor/index.d.ts +1525 -277
- package/dist/paint-editor/index.js +4561 -715
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +4 -3
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.js +10 -9
- package/dist/{svg-BnRj0q8i.d.cts → svg-CVdFc80-.d.cts} +2 -2
- package/dist/{svg-CmmBFfGT.d.ts → svg-DlEuhwoX.d.ts} +2 -2
- package/dist/{types-1SUQKDAw.d.ts → types-CDLGhAZp.d.ts} +40 -2
- package/dist/{types-yINF17Ta.d.cts → types-Tfly4Aya.d.cts} +40 -2
- package/dist/vector/index.cjs +1897 -975
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +292 -32
- package/dist/vector/index.d.ts +292 -32
- package/dist/vector/index.js +928 -6
- package/dist/vector/index.js.map +1 -1
- package/dist/video-editor/index.cjs +502 -2165
- package/dist/video-editor/index.cjs.map +1 -1
- package/dist/video-editor/index.d.cts +3 -501
- package/dist/video-editor/index.d.ts +3 -501
- package/dist/video-editor/index.js +151 -1814
- package/dist/video-editor/index.js.map +1 -1
- package/package.json +4 -2
- package/skills/pixerate-image-editor/SKILL.md +1 -0
- package/skills/pixerate-image-editor/references/color-management.md +8 -3
- package/skills/pixerate-image-editor/references/photoshop-files.md +15 -9
- package/skills/pixerate-paint-editor/SKILL.md +136 -4
- package/skills/pixerate-vector/SKILL.md +22 -1
- package/dist/chunk-5OVJHMZQ.cjs.map +0 -1
- package/dist/chunk-C6C5PDKU.js.map +0 -1
- package/dist/chunk-DFQEX3AM.cjs.map +0 -1
- package/dist/chunk-EUVSQ25B.js.map +0 -1
- package/dist/chunk-FGW5OWJ4.js.map +0 -1
- package/dist/chunk-FV5AXTSF.cjs.map +0 -1
- package/dist/chunk-GX332W2C.js +0 -181
- package/dist/chunk-GX332W2C.js.map +0 -1
- package/dist/chunk-LKCYANMW.js.map +0 -1
- package/dist/chunk-LPLCWZTM.cjs.map +0 -1
- package/dist/chunk-MUUXNRU4.cjs +0 -181
- package/dist/chunk-MUUXNRU4.cjs.map +0 -1
- package/dist/chunk-ST7E3NEE.cjs.map +0 -1
- package/dist/chunk-TCKIXI46.cjs +0 -1136
- package/dist/chunk-TCKIXI46.cjs.map +0 -1
- package/dist/chunk-UBIXVXPR.js +0 -1136
- package/dist/chunk-UBIXVXPR.js.map +0 -1
- package/dist/chunk-VXR7XWMF.js.map +0 -1
- /package/dist/{chunk-MMVHBKMM.js.map → chunk-T2P5TIZ6.js.map} +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { a1 as RasterLayer, a0 as RasterGroupLayer, a5 as RasterPixelLayer, C as CompositorLayer, a as RasterCompositor, R as RasterSources, U as LayerLocks } from '../render-CrmHOgpo.js';
|
|
2
|
-
import { B as BrushSettings, M as MixerSettings,
|
|
3
|
-
export {
|
|
2
|
+
import { D as Dab, C as CoverageMap, B as BrushSettings, e as StrokePoint, P as PaintTileTarget, T as TileMedium, M as MixerSettings, h as MixingModel, i as BlenderSettings, j as SmudgeSettings, a as MixerLoad, W as WetWorld, k as WetTilt, l as WetParams, m as WetFlowSpec, n as RgbTriple, o as WetWorldOptions, p as WetBackendInfo, q as WetSnapshot, r as WetPaperInput } from '../flow-BMtHAmg4.js';
|
|
3
|
+
export { s as DEFAULT_FLOW, t as DEFAULT_WET_PARAMS, u as MAX_BRUSH_SIZE, v as WET_FLOW_KINDS, w as WET_PARAM_KEYS, x as WetFlowKind, y as WetSupport, z as normalizeFlow, A as normalizeWetParams, E as wetSupport } from '../flow-BMtHAmg4.js';
|
|
4
4
|
import { R as RasterRect, j as TiledSurface, a as BlendMode, b as BlendModeInfo } from '../surface-Cn30xIV6.js';
|
|
5
5
|
export { B as BLEND_MODES, P as PAINTERLY_BLEND_MODES, h as Tile, v as isBlendMode, w as isPainterlyBlendMode, I as standardBlendMode } from '../surface-Cn30xIV6.js';
|
|
6
|
-
import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-
|
|
6
|
+
import { I as ImageEditorAssetStore, S as SerializedLayer, P as PsdExportResult } from '../export-Dm44YUvS.js';
|
|
7
|
+
import { V as VideoProject, A as VideoFfmpegExport } from '../export-Dv0-ZVrV.js';
|
|
7
8
|
import { g as PresenceStore, e as PresenceClock, d as PresenceActorInfo, c as PresenceActor, f as PresenceState } from '../presence-Cb6WhcrQ.js';
|
|
8
|
-
import { a as AgentToolProvider } from '../registry-D-4fit9C.js';
|
|
9
|
+
import { a as AgentToolProvider, A as AgentTool } from '../registry-D-4fit9C.js';
|
|
9
10
|
import '../types-CwMPoRMU.js';
|
|
10
11
|
import '../builtin-CY97ppKM.js';
|
|
11
12
|
import '../bezier-DYBvLgUH.js';
|
|
12
13
|
import '../types-BFkYA4eF.js';
|
|
13
14
|
import '../warp-DMN30aY6.js';
|
|
14
|
-
import '../types-
|
|
15
|
+
import '../types-CDLGhAZp.js';
|
|
15
16
|
import '../schema-Di0CS6Pe.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -92,6 +93,8 @@ interface PaperSettings {
|
|
|
92
93
|
permeability: number;
|
|
93
94
|
/** Wet media: how much the grain catches pigment, 0..1. */
|
|
94
95
|
roughness: number;
|
|
96
|
+
/** Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light, 0..1 (default 0). */
|
|
97
|
+
metallic?: number;
|
|
95
98
|
}
|
|
96
99
|
/** Side of the generated grain tile in pixels (at scale 1). */
|
|
97
100
|
declare const GRAIN_TILE = 256;
|
|
@@ -142,7 +145,365 @@ declare function renderPaper(paper: PaperSettings, rect: {
|
|
|
142
145
|
y: number;
|
|
143
146
|
width: number;
|
|
144
147
|
height: number;
|
|
145
|
-
}, grain?: PaperGrain
|
|
148
|
+
}, grain?: PaperGrain, light?: {
|
|
149
|
+
azimuth: number;
|
|
150
|
+
elevation: number;
|
|
151
|
+
}): Uint8ClampedArray;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The paint editor's dry-media stroke engine, on the shared brush engine (`@pixerate/editor/paint`):
|
|
155
|
+
*
|
|
156
|
+
* - Pen input: pressure, tilt (`tiltX` / `tiltY` or `altitude` / `azimuth`), barrel rotation
|
|
157
|
+
* (`twist`) and time (for velocity). Position and pressure are smoothed separately.
|
|
158
|
+
* - `PenDabWalker` walks the points into the shared `Dab`s at `spacing × diameter`, with every
|
|
159
|
+
* pen channel interpolated, and applies the brush's dynamics, rotation mode and jitter.
|
|
160
|
+
* Jitter is a pure hash of the stroke seed and the dab index, so a stroke replays exactly.
|
|
161
|
+
* - `DryStroke` rasterizes each dab with the shared round tip, multiplies it by how much of it the
|
|
162
|
+
* paper grain catches (`grainCatch`), and builds it up in the shared `CoverageMap`; the session
|
|
163
|
+
* then composites the coverage with the shared `applyStrokeToTile`.
|
|
164
|
+
* - Symmetry: one stroke can paint several copies of its points (mirror and kaleidoscope), each
|
|
165
|
+
* with its own walker, into the same coverage, so the copies are one stroke and one undo step.
|
|
166
|
+
*
|
|
167
|
+
* Pure data in, pixels out: no DOM, no framework.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
/** One pen sample in document pixels. Every channel but x and y is optional. */
|
|
171
|
+
interface PenPoint {
|
|
172
|
+
x: number;
|
|
173
|
+
y: number;
|
|
174
|
+
/** 0..1 (missing: 1, a mouse). */
|
|
175
|
+
pressure?: number;
|
|
176
|
+
/** Milliseconds since any fixed origin (for velocity; missing: no velocity dynamics). */
|
|
177
|
+
t?: number;
|
|
178
|
+
/** Pointer Events tilt in degrees, -90..90 (used when `altitude` / `azimuth` are missing). */
|
|
179
|
+
tiltX?: number;
|
|
180
|
+
tiltY?: number;
|
|
181
|
+
/** Radians from the surface: π/2 is upright, 0 lies flat. */
|
|
182
|
+
altitude?: number;
|
|
183
|
+
/** Radians clockwise from the +x axis (screen y points down): where the pen leans. */
|
|
184
|
+
azimuth?: number;
|
|
185
|
+
/** Barrel rotation in degrees clockwise, 0..359. */
|
|
186
|
+
twist?: number;
|
|
187
|
+
}
|
|
188
|
+
/** A pen's lean as altitude and azimuth, or `null` when the sample has no tilt. */
|
|
189
|
+
declare function penOrientation(p: PenPoint): {
|
|
190
|
+
altitude: number;
|
|
191
|
+
azimuth: number;
|
|
192
|
+
} | null;
|
|
193
|
+
/** Pointer Events `tiltX` / `tiltY` (degrees) → altitude and azimuth (radians). */
|
|
194
|
+
declare function tiltToOrientation(tiltX: number, tiltY: number): {
|
|
195
|
+
altitude: number;
|
|
196
|
+
azimuth: number;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Exponential smoothing of position and of pressure, each by its own amount (0..0.95). Feed raw
|
|
200
|
+
* samples; the first sample passes through.
|
|
201
|
+
*/
|
|
202
|
+
declare class PenSmoother {
|
|
203
|
+
private pos;
|
|
204
|
+
private pressure;
|
|
205
|
+
private readonly a;
|
|
206
|
+
private readonly ap;
|
|
207
|
+
constructor(position: number, pressure: number);
|
|
208
|
+
push(p: PenPoint): PenPoint;
|
|
209
|
+
/** Where the smoothed position is now (or `null` before the first sample). */
|
|
210
|
+
get position(): {
|
|
211
|
+
x: number;
|
|
212
|
+
y: number;
|
|
213
|
+
} | null;
|
|
214
|
+
}
|
|
215
|
+
/** A linear map of document vectors `[a, b, c, d]`: (x, y) → (a·x + b·y, c·x + d·y). */
|
|
216
|
+
type Linear2 = readonly [number, number, number, number];
|
|
217
|
+
/** How one copy of a stroke maps the input: a point map and its linear part (for dab angles). */
|
|
218
|
+
interface StrokeCopy {
|
|
219
|
+
map(p: PenPoint): PenPoint;
|
|
220
|
+
linear: Linear2;
|
|
221
|
+
}
|
|
222
|
+
declare const IDENTITY_COPY: StrokeCopy;
|
|
223
|
+
/** A dab with the pen state it was made at (the shared `Dab` plus pressure for the grain). */
|
|
224
|
+
interface PenDab extends Dab {
|
|
225
|
+
pressure: number;
|
|
226
|
+
/** Direction of travel at the dab, radians counter-clockwise with y up (as `angle`). */
|
|
227
|
+
direction: number;
|
|
228
|
+
/** The dab's index within its copy of the stroke (0, 1, 2 …). */
|
|
229
|
+
index: number;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Walks pen samples into dabs, incrementally (any chunking gives the same dabs). Dab angles are in
|
|
233
|
+
* the shared rasterizer's convention: radians, counter-clockwise with y up.
|
|
234
|
+
*/
|
|
235
|
+
declare class PenDabWalker {
|
|
236
|
+
readonly brush: PaintBrush;
|
|
237
|
+
private readonly seed;
|
|
238
|
+
private readonly linear;
|
|
239
|
+
private last;
|
|
240
|
+
private nextAt;
|
|
241
|
+
private pendingFirst;
|
|
242
|
+
private index;
|
|
243
|
+
private dir;
|
|
244
|
+
private lastT;
|
|
245
|
+
private speed;
|
|
246
|
+
constructor(brush: PaintBrush, seed?: number, linear?: Linear2);
|
|
247
|
+
private sample;
|
|
248
|
+
private diameter;
|
|
249
|
+
private stepLen;
|
|
250
|
+
/** A rasterizer angle (CCW, y up) mapped through this copy's linear part. */
|
|
251
|
+
private mapAngle;
|
|
252
|
+
private dab;
|
|
253
|
+
push(p: PenPoint, out: PenDab[]): void;
|
|
254
|
+
finish(out: PenDab[]): void;
|
|
255
|
+
}
|
|
256
|
+
interface DryStrokeOptions {
|
|
257
|
+
/** The paper's dry height field, or `null` for no grain. */
|
|
258
|
+
grain: PaperGrain | null;
|
|
259
|
+
/** The paper's grain depth, 0..1 (multiplied by the brush's grain response). */
|
|
260
|
+
paperDepth: number;
|
|
261
|
+
/** Jitter seed (record it to replay the stroke exactly). */
|
|
262
|
+
seed?: number;
|
|
263
|
+
/** Copies of the stroke (symmetry); default one identity copy. */
|
|
264
|
+
copies?: readonly StrokeCopy[];
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* One dab-engine stroke (dab and eraser brushes): smoothing, the dab walk of every copy, grain and
|
|
268
|
+
* build-up in a shared coverage map.
|
|
269
|
+
*/
|
|
270
|
+
declare class DryStroke {
|
|
271
|
+
readonly brush: PaintBrush;
|
|
272
|
+
private readonly options;
|
|
273
|
+
readonly coverage: CoverageMap;
|
|
274
|
+
dabCount: number;
|
|
275
|
+
private readonly smoother;
|
|
276
|
+
private readonly walkers;
|
|
277
|
+
private readonly copies;
|
|
278
|
+
private readonly depth;
|
|
279
|
+
private buf;
|
|
280
|
+
private lastRaw;
|
|
281
|
+
private lastFed;
|
|
282
|
+
constructor(brush: PaintBrush, options: DryStrokeOptions);
|
|
283
|
+
/** Feeds raw pen samples; returns the document rectangles the new dabs touched. */
|
|
284
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
285
|
+
/** Ends the stroke: the smoothed path catches up with the last raw sample; a single tap paints one dab. */
|
|
286
|
+
finish(): RasterRect[];
|
|
287
|
+
private paint;
|
|
288
|
+
}
|
|
289
|
+
/** The shared engine's tip for the Mixer Brush engine (it smooths and walks by itself, so smoothing is applied before). */
|
|
290
|
+
declare function mixerTip(brush: PaintBrush): BrushSettings;
|
|
291
|
+
/** Pen samples → the shared engine's stroke points (pressure only). */
|
|
292
|
+
declare function toStrokePoint(p: PenPoint): StrokePoint;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Stickers: textured stamps sprayed along a stroke onto a sticker layer, where each stays an object
|
|
296
|
+
* that can be moved, turned, resized, recoloured or deleted afterwards. A sticker layer is a pixel
|
|
297
|
+
* layer whose pixels are drawn from its stickers (`renderStickers`), so the compositor, exports and
|
|
298
|
+
* every layer operation see ordinary pixels.
|
|
299
|
+
*
|
|
300
|
+
* The stamps are our own procedural shapes (dot, star, heart, leaf, petal flower, confetti, splat,
|
|
301
|
+
* sparkle, ring), each with a little shading and a seeded paper-like texture, so no artwork is
|
|
302
|
+
* bundled. The spray (`StickerSpray`) is deterministic: the same points, seed and brush give the
|
|
303
|
+
* same stickers.
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
type StampKind = 'dot' | 'star' | 'heart' | 'leaf' | 'flower' | 'confetti' | 'splat' | 'sparkle' | 'ring';
|
|
307
|
+
declare const STAMP_KINDS: readonly StampKind[];
|
|
308
|
+
/** One sticker (document pixels). */
|
|
309
|
+
interface Sticker {
|
|
310
|
+
id: string;
|
|
311
|
+
stamp: StampKind;
|
|
312
|
+
x: number;
|
|
313
|
+
y: number;
|
|
314
|
+
/** Diameter in pixels. */
|
|
315
|
+
size: number;
|
|
316
|
+
/** Degrees clockwise. */
|
|
317
|
+
rotation: number;
|
|
318
|
+
/** #rrggbb */
|
|
319
|
+
color: string;
|
|
320
|
+
/** 0..1 */
|
|
321
|
+
opacity: number;
|
|
322
|
+
/** Texture seed. */
|
|
323
|
+
seed: number;
|
|
324
|
+
}
|
|
325
|
+
/** Sticker spray settings (each 0..1 unless noted). */
|
|
326
|
+
interface StickerSettings {
|
|
327
|
+
/** The stamps to spray (picked at random per sticker). */
|
|
328
|
+
stamps: StampKind[];
|
|
329
|
+
/** Stickers per tip diameter of travel. */
|
|
330
|
+
density: number;
|
|
331
|
+
/** Random size change. */
|
|
332
|
+
sizeJitter: number;
|
|
333
|
+
/** Random turn, as a fraction of ±180°. */
|
|
334
|
+
rotationJitter: number;
|
|
335
|
+
/** How far from the path stickers land, in tip diameters (0..2). */
|
|
336
|
+
scatter: number;
|
|
337
|
+
/** Random colour change (hue and lightness). */
|
|
338
|
+
colorJitter: number;
|
|
339
|
+
}
|
|
340
|
+
declare const DEFAULT_STICKER: Readonly<StickerSettings>;
|
|
341
|
+
declare function normalizeStickerSettings(raw?: Partial<StickerSettings>, base?: StickerSettings): StickerSettings;
|
|
342
|
+
/**
|
|
343
|
+
* Coverage of a stamp at a point (u, v) of its unit square (−1..1, y down), 0..1, before shading.
|
|
344
|
+
* `t` is the sticker's seed for shapes with random parts (splat, confetti).
|
|
345
|
+
*/
|
|
346
|
+
declare function stampCoverage(kind: StampKind, u: number, v: number, t: number): number;
|
|
347
|
+
/** The pixel rectangle a sticker covers. */
|
|
348
|
+
declare function stickerRect(s: Sticker): RasterRect;
|
|
349
|
+
/**
|
|
350
|
+
* Draws one sticker through a `TileMedium` (straight source-over): the stamp's coverage, lit a
|
|
351
|
+
* little from the top left and given a fine seeded texture, in its colour and opacity.
|
|
352
|
+
*/
|
|
353
|
+
declare function drawSticker(medium: TileMedium, s: Sticker): RasterRect;
|
|
354
|
+
interface StickerSprayInit {
|
|
355
|
+
brush: PaintBrush;
|
|
356
|
+
target: PaintTileTarget;
|
|
357
|
+
/** The paint colour #rrggbb. */
|
|
358
|
+
color: string;
|
|
359
|
+
seed: number;
|
|
360
|
+
copies: readonly StrokeCopy[];
|
|
361
|
+
/** The next sticker number on the layer (ids `st<n>`). */
|
|
362
|
+
nextId: number;
|
|
363
|
+
}
|
|
364
|
+
/** A sticker-spray stroke: drops stickers along the path and draws them as it goes. */
|
|
365
|
+
declare class StickerSpray {
|
|
366
|
+
readonly init: StickerSprayInit;
|
|
367
|
+
dabCount: number;
|
|
368
|
+
readonly stickers: Sticker[];
|
|
369
|
+
private readonly smoother;
|
|
370
|
+
private readonly walkers;
|
|
371
|
+
private readonly copies;
|
|
372
|
+
private readonly medium;
|
|
373
|
+
private lastRaw;
|
|
374
|
+
private lastFed;
|
|
375
|
+
private next;
|
|
376
|
+
constructor(init: StickerSprayInit);
|
|
377
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
378
|
+
finish(): RasterRect[];
|
|
379
|
+
private drop;
|
|
380
|
+
}
|
|
381
|
+
/** A sticker list rendered over a transparent layer, through a tile target. */
|
|
382
|
+
declare function renderStickers(stickers: readonly Sticker[], target: PaintTileTarget): void;
|
|
383
|
+
/** Checks and rounds a sticker change; throws a sentence naming the bad field. */
|
|
384
|
+
declare function normalizeStickerUpdate(u: Partial<Omit<Sticker, 'id'>>): Partial<Omit<Sticker, 'id'>>;
|
|
385
|
+
/** A sticker layer's pixels: its stickers drawn bottom first over a transparent layer. */
|
|
386
|
+
declare function stickerSurface(stickers: readonly Sticker[]): TiledSurface;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Procedural brush engines: marks made by small simulations driven by the pen, rather than by
|
|
390
|
+
* stamping the tip.
|
|
391
|
+
*
|
|
392
|
+
* - **Particle brushes** (`engine: 'particle'`): a handful of particles follow the pen by their own
|
|
393
|
+
* rules. `string`: strands of masses on springs, dragged behind the pen like hair or wet string;
|
|
394
|
+
* `spring`: each particle tied to the pen by a spring, overshooting and wobbling; `orbit`:
|
|
395
|
+
* particles circling the pen (spirograph curls); `flow`: particles thrown off the pen and carried
|
|
396
|
+
* by a swirling flow field until they fade. Each is drawn as `dot`s, a continuous `line`, or a
|
|
397
|
+
* `sequence` of beads spaced along its path.
|
|
398
|
+
* - **Bristle brushes** (`engine: 'bristle'`): a tuft of bristles, each its own thin line. Pressure
|
|
399
|
+
* splays the tuft, tilt flattens it, and bristles lag behind the tip by their stiffness, so they
|
|
400
|
+
* bend into curves at turns; each bristle carries its own paint and runs dry at its own pace, so
|
|
401
|
+
* long strokes break into dry-brush streaks.
|
|
402
|
+
*
|
|
403
|
+
* Both are deterministic (seeded) and step the simulation by distance travelled, not by how the
|
|
404
|
+
* input was sampled, so a replay paints the same marks. They build up a `CoverageMap` like the dab
|
|
405
|
+
* engine, so opacity, blend modes, locks, stencils and symmetry work as for any dry brush.
|
|
406
|
+
*/
|
|
407
|
+
|
|
408
|
+
interface ProceduralStrokeOptions {
|
|
409
|
+
grain: PaperGrain | null;
|
|
410
|
+
paperDepth: number;
|
|
411
|
+
seed?: number;
|
|
412
|
+
copies?: readonly StrokeCopy[];
|
|
413
|
+
}
|
|
414
|
+
/** What a coverage-building stroke engine provides to the stroke session. */
|
|
415
|
+
interface CoverageStroke {
|
|
416
|
+
readonly coverage: CoverageMap;
|
|
417
|
+
dabCount: number;
|
|
418
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
419
|
+
finish(): RasterRect[];
|
|
420
|
+
}
|
|
421
|
+
/** Shared plumbing: smoothing, the dab walk per copy (for size, pressure, direction), grain, and stamping marks. */
|
|
422
|
+
declare abstract class MarkStroke implements CoverageStroke {
|
|
423
|
+
readonly brush: PaintBrush;
|
|
424
|
+
protected readonly options: ProceduralStrokeOptions;
|
|
425
|
+
readonly coverage: CoverageMap;
|
|
426
|
+
dabCount: number;
|
|
427
|
+
protected readonly smoother: PenSmoother;
|
|
428
|
+
protected readonly copies: readonly StrokeCopy[];
|
|
429
|
+
protected readonly walkers: PenDabWalker[];
|
|
430
|
+
protected readonly depth: number;
|
|
431
|
+
private buf;
|
|
432
|
+
private lastRaw;
|
|
433
|
+
private lastFed;
|
|
434
|
+
protected readonly seed: number;
|
|
435
|
+
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
436
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
437
|
+
finish(): RasterRect[];
|
|
438
|
+
/** One simulation step of copy `copy` with the pen at dab `d`. */
|
|
439
|
+
protected abstract advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
440
|
+
/** Stamps one round mark of radius `r` at (x, y) with flow `alpha` towards `opacity`. */
|
|
441
|
+
protected mark(x: number, y: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
442
|
+
/** Marks along a segment, spaced by a fraction of the radius. */
|
|
443
|
+
protected segment(x0: number, y0: number, x1: number, y1: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
444
|
+
}
|
|
445
|
+
type ParticleKind = 'string' | 'spring' | 'orbit' | 'flow';
|
|
446
|
+
declare const PARTICLE_KINDS: readonly ParticleKind[];
|
|
447
|
+
type ParticleRender = 'dot' | 'line' | 'sequence';
|
|
448
|
+
declare const PARTICLE_RENDERS: readonly ParticleRender[];
|
|
449
|
+
/** Particle engine settings (each 0..1 unless noted). */
|
|
450
|
+
interface ParticleSettings {
|
|
451
|
+
kind: ParticleKind;
|
|
452
|
+
render: ParticleRender;
|
|
453
|
+
/** Particles, 1..64. */
|
|
454
|
+
count: number;
|
|
455
|
+
/** Mark size as a fraction of the tip size. */
|
|
456
|
+
size: number;
|
|
457
|
+
/** How far apart the particles start, as a fraction of the tip size. */
|
|
458
|
+
spread: number;
|
|
459
|
+
/** Spring and string stiffness (stiff follows closely; soft swings wide). */
|
|
460
|
+
stiffness: number;
|
|
461
|
+
/** How quickly motion dies down. */
|
|
462
|
+
damping: number;
|
|
463
|
+
/** Orbit and flow speed. */
|
|
464
|
+
speed: number;
|
|
465
|
+
/** Pull downwards (−1 up .. 1 down). */
|
|
466
|
+
gravity: number;
|
|
467
|
+
/** Flow particles' lifetime. */
|
|
468
|
+
life: number;
|
|
469
|
+
/** Sequence: spacing of the beads along each path. */
|
|
470
|
+
interval: number;
|
|
471
|
+
}
|
|
472
|
+
declare const DEFAULT_PARTICLE: Readonly<ParticleSettings>;
|
|
473
|
+
declare function normalizeParticle(raw?: Partial<ParticleSettings>, base?: ParticleSettings): ParticleSettings;
|
|
474
|
+
/** A particle-brush stroke. */
|
|
475
|
+
declare class ParticleStroke extends MarkStroke {
|
|
476
|
+
private readonly sets;
|
|
477
|
+
private readonly s;
|
|
478
|
+
private spawn;
|
|
479
|
+
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
480
|
+
private init;
|
|
481
|
+
protected advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
482
|
+
}
|
|
483
|
+
/** Bristle engine settings (each 0..1 unless noted). */
|
|
484
|
+
interface BristleSettings {
|
|
485
|
+
/** Bristles in the tuft, 3..128. */
|
|
486
|
+
count: number;
|
|
487
|
+
/** Stiff bristles follow the tip; soft ones lag and bend into curves at turns. */
|
|
488
|
+
stiffness: number;
|
|
489
|
+
/** How far pressure spreads the tuft. */
|
|
490
|
+
splay: number;
|
|
491
|
+
/** Width of each bristle's line as a fraction of the tip size. */
|
|
492
|
+
thickness: number;
|
|
493
|
+
/** Paint per bristle: 1 never runs dry; lower, bristles run out along the stroke (dry brush). */
|
|
494
|
+
load: number;
|
|
495
|
+
/** Random variation between bristles (length, paint, position). */
|
|
496
|
+
jitter: number;
|
|
497
|
+
}
|
|
498
|
+
declare const DEFAULT_BRISTLE: Readonly<BristleSettings>;
|
|
499
|
+
declare function normalizeBristle(raw?: Partial<BristleSettings>, base?: BristleSettings): BristleSettings;
|
|
500
|
+
/** A bristle-brush stroke: every bristle draws its own line. */
|
|
501
|
+
declare class BristleStroke extends MarkStroke {
|
|
502
|
+
private readonly tufts;
|
|
503
|
+
private readonly s;
|
|
504
|
+
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
505
|
+
protected advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
506
|
+
}
|
|
146
507
|
|
|
147
508
|
/**
|
|
148
509
|
* Paint editor brushes: the shared engine's tip settings (`@pixerate/editor/paint`) plus what a
|
|
@@ -156,10 +517,74 @@ declare function renderPaper(paper: PaperSettings, rect: {
|
|
|
156
517
|
* mix exchange when its behaviour asks for it), `eraser` (removes alpha), `mixer` (the Mixer
|
|
157
518
|
* Brush), `blender` (mixes the colours under and near the tip) or `smudge` (drags paint along).
|
|
158
519
|
*/
|
|
159
|
-
type PaintBrushEngine = 'dab' | 'eraser' | 'mixer' | 'blender' | 'smudge';
|
|
520
|
+
type PaintBrushEngine = 'dab' | 'eraser' | 'mixer' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker';
|
|
160
521
|
declare const PAINT_BRUSH_ENGINES: readonly PaintBrushEngine[];
|
|
161
|
-
type PaintBrushCategory = 'pencil' | 'pastel' | 'charcoal' | 'airbrush' | 'marker' | 'ink' | 'paint' | 'eraser' | 'blender' | 'smudge' | 'mixer' | 'custom';
|
|
522
|
+
type PaintBrushCategory = 'pencil' | 'pastel' | 'charcoal' | 'airbrush' | 'marker' | 'ink' | 'paint' | 'eraser' | 'blender' | 'smudge' | 'mixer' | 'watercolor' | 'wet-tool' | 'oil' | 'impasto' | 'procedural' | 'bristle' | 'sticker' | 'custom';
|
|
162
523
|
declare const PAINT_BRUSH_CATEGORIES: readonly PaintBrushCategory[];
|
|
524
|
+
/**
|
|
525
|
+
* What a wet brush does to the wet layer: `paint` (water and pigment), `water` (clear water;
|
|
526
|
+
* with `rewet` it lifts dried paint), `dry` (a hair dryer: takes water, the pigment settles),
|
|
527
|
+
* `blow` (pushes the water along the stroke), `blend` (evens out the wet pigment under the tip),
|
|
528
|
+
* `smudge` (drags wet paint along) or `blot` (a tissue: takes up wet paint).
|
|
529
|
+
*/
|
|
530
|
+
type WetTool = 'paint' | 'water' | 'dry' | 'blow' | 'blend' | 'smudge' | 'blot';
|
|
531
|
+
declare const WET_TOOLS: readonly WetTool[];
|
|
532
|
+
/**
|
|
533
|
+
* The wet medium a wet brush paints: its layer takes the medium's wet settings the first time it
|
|
534
|
+
* holds wet paint (`watercolor`: runny, dries in seconds, granulates and darkens at edges;
|
|
535
|
+
* `gouache`: opaque, flatter; `oil`: thick, barely flows, stays wet for a long time and blends into
|
|
536
|
+
* the wet paint it meets).
|
|
537
|
+
*/
|
|
538
|
+
type WetMediumKind = 'watercolor' | 'gouache' | 'oil';
|
|
539
|
+
declare const WET_MEDIA: readonly WetMediumKind[];
|
|
540
|
+
/** Wet engine settings, each 0..1. */
|
|
541
|
+
interface WetBrushSettings {
|
|
542
|
+
tool: WetTool;
|
|
543
|
+
/** Water the brush lays down (wetness): 0.2 a damp brush, 1 a dripping one. */
|
|
544
|
+
water: number;
|
|
545
|
+
/** Pigment concentration: 0 clear water .. 1 paint straight from the tube. */
|
|
546
|
+
pigment: number;
|
|
547
|
+
/** Body: 0 a transparent watercolour .. 1 opaque gouache that hides what is under it. */
|
|
548
|
+
body: number;
|
|
549
|
+
/** Strength of the dry, blow, blend, smudge and blot tools. */
|
|
550
|
+
strength: number;
|
|
551
|
+
/** How much dried paint the water lifts again (re-wetting): 0 none (staining) .. 1. */
|
|
552
|
+
rewet: number;
|
|
553
|
+
/** The medium (its layer's wet settings the first time it holds wet paint). */
|
|
554
|
+
medium: WetMediumKind;
|
|
555
|
+
/** Paint loading: 1 never runs out; lower, the brush runs dry along a long stroke (reload each stroke). */
|
|
556
|
+
load: number;
|
|
557
|
+
/** Thinner (water for watercolour, solvent for oil): thins the paint, so it is more transparent and runnier. */
|
|
558
|
+
thinner: number;
|
|
559
|
+
}
|
|
560
|
+
declare const DEFAULT_WET_BRUSH: Readonly<WetBrushSettings>;
|
|
561
|
+
declare function normalizeWetBrush(raw?: Partial<WetBrushSettings>, base?: WetBrushSettings): WetBrushSettings;
|
|
562
|
+
/**
|
|
563
|
+
* What an impasto brush does on a thick-paint layer: `brush` (paint with body: colour and relief),
|
|
564
|
+
* `tube` (squeezes blobs of paint straight from the tube), `knife` (a palette knife: scrapes the
|
|
565
|
+
* relief flat and smears the paint along), `roller` (a flat, even band with a stipple), `gloop`
|
|
566
|
+
* (a thick, glossy, rounded line).
|
|
567
|
+
*/
|
|
568
|
+
type ImpastoTool = 'brush' | 'tube' | 'knife' | 'roller' | 'gloop';
|
|
569
|
+
declare const IMPASTO_TOOLS: readonly ImpastoTool[];
|
|
570
|
+
/** Impasto engine settings, each 0..1. */
|
|
571
|
+
interface ImpastoSettings {
|
|
572
|
+
tool: ImpastoTool;
|
|
573
|
+
/** How thick the paint stands up (relief per stroke). */
|
|
574
|
+
thickness: number;
|
|
575
|
+
/** Shine of the paint when lit (0 matte .. 1 wet-looking gloss). */
|
|
576
|
+
gloss: number;
|
|
577
|
+
/** Metallic paint: highlights take the paint's colour (gold, copper, silver). */
|
|
578
|
+
metallic: number;
|
|
579
|
+
/** Paint loading: 1 never runs out; lower, the stroke thins and fades as the brush empties. */
|
|
580
|
+
load: number;
|
|
581
|
+
/** Thinner: flatter, more transparent paint that levels out. */
|
|
582
|
+
thinner: number;
|
|
583
|
+
/** Knife and brush: how much of the paint under the tool is dragged along (0..1). */
|
|
584
|
+
drag: number;
|
|
585
|
+
}
|
|
586
|
+
declare const DEFAULT_IMPASTO: Readonly<ImpastoSettings>;
|
|
587
|
+
declare function normalizeImpasto(raw?: Partial<ImpastoSettings>, base?: ImpastoSettings): ImpastoSettings;
|
|
163
588
|
/** Multi-coloured brush: `tip` lays the colours across the tip, `dab` changes colour from dab to dab. */
|
|
164
589
|
type MultiColorMode = 'off' | 'tip' | 'dab';
|
|
165
590
|
declare const MULTI_COLOR_MODES: readonly MultiColorMode[];
|
|
@@ -247,6 +672,16 @@ interface PaintBrush {
|
|
|
247
672
|
blender: BlenderSettings;
|
|
248
673
|
/** Smudge engine only: strength, length and mixing. */
|
|
249
674
|
smudge: SmudgeSettings;
|
|
675
|
+
/** Wet engine only: the wet tool, water, pigment, body and strength. */
|
|
676
|
+
wet: WetBrushSettings;
|
|
677
|
+
/** Impasto engine only: the tool, thickness, gloss, metallic, loading, thinner and drag. */
|
|
678
|
+
impasto: ImpastoSettings;
|
|
679
|
+
/** Particle engine only: the particles' kind, how they are drawn, and their physics. */
|
|
680
|
+
particle: ParticleSettings;
|
|
681
|
+
/** Bristle engine only: the tuft. */
|
|
682
|
+
bristle: BristleSettings;
|
|
683
|
+
/** Sticker engine only: the stamps and how they are sprayed. */
|
|
684
|
+
sticker: StickerSettings;
|
|
250
685
|
/** True for the built-in presets (they cannot be removed or overwritten). */
|
|
251
686
|
builtin?: boolean;
|
|
252
687
|
}
|
|
@@ -259,16 +694,23 @@ type BrushBehaviorInput = Partial<Omit<BrushBehavior, 'multicolor'>> & {
|
|
|
259
694
|
};
|
|
260
695
|
declare function normalizeBehavior(raw?: BrushBehaviorInput, base?: BrushBehavior): BrushBehavior;
|
|
261
696
|
/** Brush settings to change or read: any subset of a brush and of its blocks. */
|
|
262
|
-
type PaintBrushInput = Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer' | 'behavior' | 'blender' | 'smudge'>> & {
|
|
697
|
+
type PaintBrushInput = Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer' | 'behavior' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker'>> & {
|
|
263
698
|
tip?: Partial<BrushSettings>;
|
|
264
699
|
dynamics?: Partial<BrushDynamics>;
|
|
265
700
|
mixer?: Partial<MixerSettings>;
|
|
266
701
|
behavior?: BrushBehaviorInput;
|
|
267
702
|
blender?: Partial<BlenderSettings>;
|
|
268
703
|
smudge?: Partial<SmudgeSettings>;
|
|
704
|
+
wet?: Partial<WetBrushSettings>;
|
|
705
|
+
impasto?: Partial<ImpastoSettings>;
|
|
706
|
+
particle?: Partial<ParticleSettings>;
|
|
707
|
+
bristle?: Partial<BristleSettings>;
|
|
708
|
+
sticker?: Partial<StickerSettings>;
|
|
269
709
|
};
|
|
270
|
-
/** Whether a brush can pick paint up from the canvas (the pick-up / mix ratios apply): dab and
|
|
710
|
+
/** Whether a brush can pick paint up from the canvas (the pick-up / mix ratios apply): dab, mixer and wet brushes. */
|
|
271
711
|
declare function isWetCapable(brush: Pick<PaintBrush, 'engine'>): boolean;
|
|
712
|
+
/** Whether a brush paints wet media (on the layer's wet layer). */
|
|
713
|
+
declare function isWetBrush(brush: Pick<PaintBrush, 'engine'>): boolean;
|
|
272
714
|
/**
|
|
273
715
|
* Whether a dab brush paints dab by dab through the exchange (it picks up paint or is
|
|
274
716
|
* multi-coloured) instead of building one coverage buffer of one colour.
|
|
@@ -295,145 +737,207 @@ interface BrushLibraryFile {
|
|
|
295
737
|
declare function parseBrushLibrary(raw: unknown, problems?: string[]): PaintBrush[];
|
|
296
738
|
|
|
297
739
|
/**
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* - `PenDabWalker` walks the points into the shared `Dab`s at `spacing × diameter`, with every
|
|
303
|
-
* pen channel interpolated, and applies the brush's dynamics, rotation mode and jitter.
|
|
304
|
-
* Jitter is a pure hash of the stroke seed and the dab index, so a stroke replays exactly.
|
|
305
|
-
* - `DryStroke` rasterizes each dab with the shared round tip, multiplies it by how much of it the
|
|
306
|
-
* paper grain catches (`grainCatch`), and builds it up in the shared `CoverageMap`; the session
|
|
307
|
-
* then composites the coverage with the shared `applyStrokeToTile`.
|
|
308
|
-
* - Symmetry: one stroke can paint several copies of its points (mirror and kaleidoscope), each
|
|
309
|
-
* with its own walker, into the same coverage, so the copies are one stroke and one undo step.
|
|
310
|
-
*
|
|
311
|
-
* Pure data in, pixels out: no DOM, no framework.
|
|
740
|
+
* Painting guides: mirror and kaleidoscope symmetry (every stroke paints its reflected or rotated
|
|
741
|
+
* copies as one stroke), perspective guides with one to three vanishing points that strokes snap
|
|
742
|
+
* to, a tracing image under the paint (optionally the source of the stroke colour), and floating
|
|
743
|
+
* reference images to pick colours from. Geometry only: no pixels, no DOM.
|
|
312
744
|
*/
|
|
313
745
|
|
|
314
|
-
|
|
315
|
-
|
|
746
|
+
type SymmetryMode = 'off' | 'vertical' | 'horizontal' | 'dual' | 'diagonal' | 'kaleidoscope';
|
|
747
|
+
declare const SYMMETRY_MODES: readonly SymmetryMode[];
|
|
748
|
+
interface SymmetryGuide {
|
|
749
|
+
mode: SymmetryMode;
|
|
750
|
+
/** Centre of the axes in document pixels (default the canvas centre). */
|
|
751
|
+
center: {
|
|
752
|
+
x: number;
|
|
753
|
+
y: number;
|
|
754
|
+
};
|
|
755
|
+
/** Turns the axes, degrees clockwise (default 0). */
|
|
756
|
+
rotation: number;
|
|
757
|
+
/** Kaleidoscope: number of segments, 2..32 (default 6). */
|
|
758
|
+
segments: number;
|
|
759
|
+
/** Kaleidoscope: reflect every other segment as well as rotating (default true), like a real kaleidoscope. */
|
|
760
|
+
mirror: boolean;
|
|
761
|
+
}
|
|
762
|
+
interface Point2 {
|
|
316
763
|
x: number;
|
|
317
764
|
y: number;
|
|
318
|
-
/** 0..1 (missing: 1, a mouse). */
|
|
319
|
-
pressure?: number;
|
|
320
|
-
/** Milliseconds since any fixed origin (for velocity; missing: no velocity dynamics). */
|
|
321
|
-
t?: number;
|
|
322
|
-
/** Pointer Events tilt in degrees, -90..90 (used when `altitude` / `azimuth` are missing). */
|
|
323
|
-
tiltX?: number;
|
|
324
|
-
tiltY?: number;
|
|
325
|
-
/** Radians from the surface: π/2 is upright, 0 lies flat. */
|
|
326
|
-
altitude?: number;
|
|
327
|
-
/** Radians clockwise from the +x axis (screen y points down): where the pen leans. */
|
|
328
|
-
azimuth?: number;
|
|
329
|
-
/** Barrel rotation in degrees clockwise, 0..359. */
|
|
330
|
-
twist?: number;
|
|
331
765
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
private pressure;
|
|
349
|
-
private readonly a;
|
|
350
|
-
private readonly ap;
|
|
351
|
-
constructor(position: number, pressure: number);
|
|
352
|
-
push(p: PenPoint): PenPoint;
|
|
353
|
-
/** Where the smoothed position is now (or `null` before the first sample). */
|
|
354
|
-
get position(): {
|
|
766
|
+
interface PerspectiveGuide {
|
|
767
|
+
enabled: boolean;
|
|
768
|
+
/** One to three vanishing points in document pixels (they may lie outside the canvas). */
|
|
769
|
+
points: Point2[];
|
|
770
|
+
/** Strokes snap to the guide lines (default true when enabled). */
|
|
771
|
+
snap: boolean;
|
|
772
|
+
}
|
|
773
|
+
/** A view-only image under the paint, placed over the canvas. */
|
|
774
|
+
interface TracingImage {
|
|
775
|
+
/** PNG or JPEG data URL. */
|
|
776
|
+
src: string;
|
|
777
|
+
/** Natural size of the image in pixels. */
|
|
778
|
+
width: number;
|
|
779
|
+
height: number;
|
|
780
|
+
/** Where it is drawn, in document pixels (default: fitted to the canvas). */
|
|
781
|
+
rect: {
|
|
355
782
|
x: number;
|
|
356
783
|
y: number;
|
|
357
|
-
|
|
784
|
+
width: number;
|
|
785
|
+
height: number;
|
|
786
|
+
};
|
|
787
|
+
/** 0..1 (default 0.35). */
|
|
788
|
+
opacity: number;
|
|
789
|
+
visible: boolean;
|
|
790
|
+
/** Strokes without an explicit colour take the tracing image's colour where they start. */
|
|
791
|
+
sampleColors: boolean;
|
|
358
792
|
}
|
|
359
|
-
/** A
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
793
|
+
/** A floating image to look at and pick colours from (shown beside the canvas, never painted). */
|
|
794
|
+
interface ReferenceImage {
|
|
795
|
+
id: string;
|
|
796
|
+
name: string;
|
|
797
|
+
src: string;
|
|
798
|
+
width: number;
|
|
799
|
+
height: number;
|
|
800
|
+
/** Where the panel floats over the viewport, in screen pixels, and its displayed width. */
|
|
801
|
+
x: number;
|
|
802
|
+
y: number;
|
|
803
|
+
displayWidth: number;
|
|
804
|
+
visible: boolean;
|
|
365
805
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
index: number;
|
|
806
|
+
interface PaintGuides {
|
|
807
|
+
symmetry: SymmetryGuide;
|
|
808
|
+
perspective: PerspectiveGuide;
|
|
809
|
+
tracing: TracingImage | null;
|
|
810
|
+
references: ReferenceImage[];
|
|
811
|
+
/** Rulers, arcs, straight edges and shape templates that guide or mask strokes. */
|
|
812
|
+
stencils: Stencil[];
|
|
374
813
|
}
|
|
814
|
+
declare function defaultGuides(canvas: {
|
|
815
|
+
width: number;
|
|
816
|
+
height: number;
|
|
817
|
+
}): PaintGuides;
|
|
818
|
+
declare const MAX_COORDINATE = 1000000;
|
|
819
|
+
declare function normalizeSymmetry(raw: Partial<SymmetryGuide> | Record<string, unknown>, base: SymmetryGuide): SymmetryGuide;
|
|
820
|
+
/** The axis directions of a mirror mode (radians), as photocraft's `directions`. */
|
|
821
|
+
declare function symmetryAxisAngles(g: SymmetryGuide): number[];
|
|
375
822
|
/**
|
|
376
|
-
*
|
|
377
|
-
*
|
|
823
|
+
* The extra copies a stroke paints under the symmetry guide, as affine maps (the stroke itself is
|
|
824
|
+
* not included). Vertical, horizontal and diagonal: one reflection. Dual: two reflections and the
|
|
825
|
+
* point reflection. Kaleidoscope: rotations by 360°/n, and with `mirror` the reflections between them.
|
|
378
826
|
*/
|
|
379
|
-
declare
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
827
|
+
declare function symmetryCopies(g: SymmetryGuide): Affine[];
|
|
828
|
+
/** The axes clipped to the canvas, for drawing (photocraft's `segments`). */
|
|
829
|
+
declare function symmetryAxisSegments(g: SymmetryGuide, size: {
|
|
830
|
+
width: number;
|
|
831
|
+
height: number;
|
|
832
|
+
}): Array<[Point2, Point2]>;
|
|
833
|
+
declare function normalizePerspective(raw: Partial<PerspectiveGuide> | Record<string, unknown>, base: PerspectiveGuide): PerspectiveGuide;
|
|
834
|
+
/** The line families a perspective guide snaps to at `p`: unit directions. */
|
|
835
|
+
declare function perspectiveDirections(g: PerspectiveGuide, p: Point2): Point2[];
|
|
836
|
+
/** How far the pen must move before a stroke chooses its perspective line (document pixels). */
|
|
837
|
+
declare const SNAP_DECIDE_DISTANCE = 6;
|
|
838
|
+
/**
|
|
839
|
+
* Snaps one stroke to a perspective guide: once the pen has moved `SNAP_DECIDE_DISTANCE` from
|
|
840
|
+
* where it started, the stroke picks the guide line through its start that best matches its
|
|
841
|
+
* direction, and every sample after that is projected onto the line.
|
|
842
|
+
*/
|
|
843
|
+
declare class PerspectiveSnapper {
|
|
844
|
+
private readonly guide;
|
|
845
|
+
private start;
|
|
387
846
|
private dir;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
private mapAngle;
|
|
396
|
-
private dab;
|
|
397
|
-
push(p: PenPoint, out: PenDab[]): void;
|
|
398
|
-
finish(out: PenDab[]): void;
|
|
847
|
+
constructor(guide: PerspectiveGuide);
|
|
848
|
+
/** The line the stroke follows, once chosen. */
|
|
849
|
+
get line(): {
|
|
850
|
+
from: Point2;
|
|
851
|
+
direction: Point2;
|
|
852
|
+
} | null;
|
|
853
|
+
snap<P extends Point2>(p: P): P;
|
|
399
854
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
855
|
+
/** Guide lines to draw for a perspective guide: rays from each vanishing point through the canvas. */
|
|
856
|
+
declare function perspectiveGuideLines(g: PerspectiveGuide, size: {
|
|
857
|
+
width: number;
|
|
858
|
+
height: number;
|
|
859
|
+
}, count?: number): Array<[Point2, Point2]>;
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Stencils: shapes laid on the canvas that guide or mask strokes, like a ruler, a French curve or a
|
|
863
|
+
* masking template held over the paper.
|
|
864
|
+
*
|
|
865
|
+
* - `ruler` (a straight edge) and `arc` (a circle or part of one) **guide**: a stroke that starts
|
|
866
|
+
* within `snap` pixels of the edge follows it exactly; one that starts further away is free.
|
|
867
|
+
* - `line` (a straight edge across the canvas) and `shape` (rectangle, ellipse or polygon)
|
|
868
|
+
* **mask**: paint lands only on one side of the line, or only inside the shape (outside with
|
|
869
|
+
* `invert`), with an anti-aliased edge. Several masks combine (paint lands where all allow it).
|
|
870
|
+
* - Any stencil can be switched to the other `mode`: a shape can guide (strokes follow its
|
|
871
|
+
* outline) and a ruler can mask (paint on one side of it).
|
|
872
|
+
*
|
|
873
|
+
* Geometry only; `stencilMask` makes the canvas coverage the stroke engines read as a selection.
|
|
874
|
+
*/
|
|
875
|
+
|
|
876
|
+
type StencilKind = 'ruler' | 'arc' | 'line' | 'shape';
|
|
877
|
+
declare const STENCIL_KINDS: readonly StencilKind[];
|
|
878
|
+
type StencilShape = 'rect' | 'ellipse' | 'polygon';
|
|
879
|
+
declare const STENCIL_SHAPES: readonly StencilShape[];
|
|
880
|
+
interface Stencil {
|
|
881
|
+
id: string;
|
|
882
|
+
kind: StencilKind;
|
|
883
|
+
/** `guide`: strokes that start near the edge follow it; `mask`: paint lands only inside (or on one side). */
|
|
884
|
+
mode: 'guide' | 'mask';
|
|
885
|
+
/** Shown and in effect (default true). */
|
|
886
|
+
enabled: boolean;
|
|
887
|
+
/** ruler / line: two points on the edge (document pixels). */
|
|
888
|
+
from?: Point2;
|
|
889
|
+
to?: Point2;
|
|
890
|
+
/** arc: centre and radius, and the part of the circle in degrees clockwise from +x (default the whole circle). */
|
|
891
|
+
center?: Point2;
|
|
892
|
+
radius?: number;
|
|
893
|
+
startAngle?: number;
|
|
894
|
+
endAngle?: number;
|
|
895
|
+
/** shape: its kind and geometry (`rect` for rect and ellipse, `points` for a polygon). */
|
|
896
|
+
shape?: StencilShape;
|
|
897
|
+
rect?: {
|
|
898
|
+
x: number;
|
|
899
|
+
y: number;
|
|
900
|
+
width: number;
|
|
901
|
+
height: number;
|
|
902
|
+
};
|
|
903
|
+
points?: Point2[];
|
|
904
|
+
/** mask: paint outside the shape (or on the other side of the line) instead. */
|
|
905
|
+
invert: boolean;
|
|
906
|
+
/** guide: how near the edge (px) a stroke must start to follow it (default 40). */
|
|
907
|
+
snap: number;
|
|
908
|
+
/** mask: soft edge width in pixels (default 1, anti-aliased). */
|
|
909
|
+
feather: number;
|
|
409
910
|
}
|
|
911
|
+
type StencilInput = Partial<Omit<Stencil, 'id'>> & {
|
|
912
|
+
kind: StencilKind;
|
|
913
|
+
};
|
|
914
|
+
/** A stencil with every field in range; throws a sentence naming what is missing. */
|
|
915
|
+
declare function normalizeStencil(raw: StencilInput, id: string, base?: Stencil): Stencil;
|
|
916
|
+
/** The outline of a stencil as a polyline (document pixels), for drawing and for shape guides. */
|
|
917
|
+
declare function stencilOutline(s: Stencil): Point2[];
|
|
410
918
|
/**
|
|
411
|
-
*
|
|
412
|
-
*
|
|
919
|
+
* Snaps a stroke to the guide stencils: the first point picks the nearest guide within its `snap`
|
|
920
|
+
* distance (or none), and every point after it is moved onto that guide's edge.
|
|
413
921
|
*/
|
|
414
|
-
declare class
|
|
415
|
-
readonly
|
|
416
|
-
private
|
|
417
|
-
readonly
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
private readonly copies;
|
|
422
|
-
private readonly depth;
|
|
423
|
-
private buf;
|
|
424
|
-
private lastRaw;
|
|
425
|
-
private lastFed;
|
|
426
|
-
constructor(brush: PaintBrush, options: DryStrokeOptions);
|
|
427
|
-
/** Feeds raw pen samples; returns the document rectangles the new dabs touched. */
|
|
428
|
-
push(points: readonly PenPoint[]): RasterRect[];
|
|
429
|
-
/** Ends the stroke: the smoothed path catches up with the last raw sample; a single tap paints one dab. */
|
|
430
|
-
finish(): RasterRect[];
|
|
431
|
-
private paint;
|
|
922
|
+
declare class StencilSnapper {
|
|
923
|
+
private readonly stencils;
|
|
924
|
+
private guide;
|
|
925
|
+
constructor(stencils: readonly Stencil[]);
|
|
926
|
+
/** The stencil the stroke follows, once chosen (`null`: none). */
|
|
927
|
+
get following(): Stencil | null;
|
|
928
|
+
snap<P extends Point2>(p: P): P;
|
|
432
929
|
}
|
|
433
|
-
/**
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
930
|
+
/**
|
|
931
|
+
* The combined coverage of the enabled mask stencils over a `width × height` canvas (0..255 per
|
|
932
|
+
* pixel), or `null` when no stencil masks.
|
|
933
|
+
*/
|
|
934
|
+
declare function stencilMask(stencils: readonly Stencil[], width: number, height: number): {
|
|
935
|
+
width: number;
|
|
936
|
+
height: number;
|
|
937
|
+
data: Uint8Array;
|
|
938
|
+
} | null;
|
|
939
|
+
/** A stencil scaled about the origin by `k` (for repainting at a larger size). */
|
|
940
|
+
declare function scaleStencil(s: Stencil, k: number): Stencil;
|
|
437
941
|
|
|
438
942
|
/**
|
|
439
943
|
* Strokes as data. Every committed stroke is recorded with the brush, colour, raw pen samples,
|
|
@@ -457,6 +961,10 @@ interface RecordedStroke {
|
|
|
457
961
|
copies?: Affine[];
|
|
458
962
|
/** What the brush carried at the start (Mixer Brush and wet-capable brushes), so a replay mixes the same. */
|
|
459
963
|
load?: MixerLoad;
|
|
964
|
+
/** Wet brushes: the wet simulation's clock (steps) at which each point was applied, so a replay lets the paint flow the same. */
|
|
965
|
+
steps?: number[];
|
|
966
|
+
/** The mask stencils it was painted through (so a replay keeps to them). */
|
|
967
|
+
stencils?: Stencil[];
|
|
460
968
|
}
|
|
461
969
|
declare function applyAffine(m: Affine, x: number, y: number): {
|
|
462
970
|
x: number;
|
|
@@ -496,13 +1004,339 @@ interface StrokeVariation {
|
|
|
496
1004
|
/** Multiply every pressure by this (default 1). */
|
|
497
1005
|
pressureScale?: number;
|
|
498
1006
|
}
|
|
499
|
-
/** The affine map of a variation about `origin`. */
|
|
500
|
-
declare function variationAffine(v: StrokeVariation, origin: {
|
|
501
|
-
x: number;
|
|
502
|
-
y: number;
|
|
503
|
-
}): Affine;
|
|
504
|
-
/** A stroke varied by `m` (points mapped; symmetry copies conjugated so they stay symmetric). */
|
|
505
|
-
declare function varyStroke(stroke: RecordedStroke, m: Affine, v: StrokeVariation): RecordedStroke;
|
|
1007
|
+
/** The affine map of a variation about `origin`. */
|
|
1008
|
+
declare function variationAffine(v: StrokeVariation, origin: {
|
|
1009
|
+
x: number;
|
|
1010
|
+
y: number;
|
|
1011
|
+
}): Affine;
|
|
1012
|
+
/** A stroke varied by `m` (points mapped; symmetry copies conjugated so they stay symmetric). */
|
|
1013
|
+
declare function varyStroke(stroke: RecordedStroke, m: Affine, v: StrokeVariation): RecordedStroke;
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Wet media in the paint editor (watercolour first): a layer painted with a wet brush gets a wet
|
|
1017
|
+
* layer in the document's `WetWorld` (`@pixerate/editor/paint`), and its pixels are the world's
|
|
1018
|
+
* display of it until the paint dries.
|
|
1019
|
+
*
|
|
1020
|
+
* - **Wet brushes** (`engine: 'wet'`) run through `WetStrokeDriver`: the same pen input as dry
|
|
1021
|
+
* media (`PenDabWalker`: pressure, tilt, rotation, jitter, symmetry copies), each dab's tip
|
|
1022
|
+
* coverage (quantised to 1/4096, so tiny floating-point differences between engines never reach
|
|
1023
|
+
* the simulation) applied by the brush's wet tool. `paint` goes through the shared pick-up / mix
|
|
1024
|
+
* exchange over a `WetMedium`, so a wet brush lifts and mixes wet paint like the phase 2 brushes.
|
|
1025
|
+
* - **Time** is the world's step clock. The host calls `tick` every frame (the bindings do); it
|
|
1026
|
+
* steps at `speed` steps per second unless paused. Agents call `advance` or `dry`.
|
|
1027
|
+
* - **Dabs between steps.** Edits only happen between batches: points pushed while a batch runs
|
|
1028
|
+
* (in Web Workers) wait for it, and every raw point records the clock it was applied at
|
|
1029
|
+
* (`RecordedStroke.steps`), so a stroke replays exactly.
|
|
1030
|
+
* - **Drying.** Tiles that have dried are baked into the layer's pixels by the world; when a whole
|
|
1031
|
+
* layer is dry its wet layer is dropped and it is an ordinary pixel layer again. `dry` dries at
|
|
1032
|
+
* once. Saving and exporting use the deterministic bake of everything still wet ("wet paint is
|
|
1033
|
+
* dried on save": the document holds dried pixels, never wet state).
|
|
1034
|
+
* - **Undo.** Each wet step of history keeps a snapshot of the world before and after it, within
|
|
1035
|
+
* `undoMemory` bytes; older snapshots are dropped, and undoing past them leaves that paint dry
|
|
1036
|
+
* as it looked.
|
|
1037
|
+
*/
|
|
1038
|
+
|
|
1039
|
+
/** The paper as the wet simulation reads it. */
|
|
1040
|
+
declare function wetPaperInput(paper: PaperSettings): WetPaperInput;
|
|
1041
|
+
interface WetStrokeInit {
|
|
1042
|
+
world: WetWorld;
|
|
1043
|
+
laneId: string;
|
|
1044
|
+
brush: PaintBrush;
|
|
1045
|
+
/** Paint colour, sRGB 0..1. */
|
|
1046
|
+
color: RgbTriple;
|
|
1047
|
+
load: MixerLoad;
|
|
1048
|
+
grain: PaperGrain | null;
|
|
1049
|
+
paperDepth: number;
|
|
1050
|
+
paperColor: readonly [number, number, number];
|
|
1051
|
+
seed: number;
|
|
1052
|
+
copies: readonly StrokeCopy[];
|
|
1053
|
+
/** Where paint may land (canvas-sized coverage 0..255: the stencils' mask), or none. */
|
|
1054
|
+
mask?: {
|
|
1055
|
+
width: number;
|
|
1056
|
+
height: number;
|
|
1057
|
+
data: ArrayLike<number>;
|
|
1058
|
+
} | null;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* One wet-brush stroke: pen samples → dabs → wet tool edits. Points are applied only while the
|
|
1062
|
+
* world is between batches (`flush` applies the ones that waited).
|
|
1063
|
+
*/
|
|
1064
|
+
declare class WetStrokeDriver {
|
|
1065
|
+
readonly init: WetStrokeInit;
|
|
1066
|
+
dabCount: number;
|
|
1067
|
+
bounds: RasterRect | null;
|
|
1068
|
+
/** The world's clock at which each raw point was applied (recorded with the stroke). */
|
|
1069
|
+
readonly steps: number[];
|
|
1070
|
+
private readonly smoother;
|
|
1071
|
+
private readonly states;
|
|
1072
|
+
private readonly medium;
|
|
1073
|
+
private readonly queue;
|
|
1074
|
+
private finishing;
|
|
1075
|
+
private finished;
|
|
1076
|
+
private lastRaw;
|
|
1077
|
+
private lastFed;
|
|
1078
|
+
private tip;
|
|
1079
|
+
private cov;
|
|
1080
|
+
private readonly paint;
|
|
1081
|
+
private readonly depth;
|
|
1082
|
+
constructor(init: WetStrokeInit);
|
|
1083
|
+
private readonly copies;
|
|
1084
|
+
/** What copy 0's brush carries after the dabs so far. */
|
|
1085
|
+
get load(): MixerLoad | null;
|
|
1086
|
+
/** Whether every point (and the end) has been applied. */
|
|
1087
|
+
get done(): boolean;
|
|
1088
|
+
/** Adds raw points; applies them now unless a batch is running. Returns where pixels changed. */
|
|
1089
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
1090
|
+
/** Asks for the end; it happens now unless a batch is running (then at the next `flush`). */
|
|
1091
|
+
finish(): RasterRect[];
|
|
1092
|
+
/** Applies waiting points (and the end, when asked) if the world is between batches. */
|
|
1093
|
+
flush(): RasterRect[];
|
|
1094
|
+
private applyDabs;
|
|
1095
|
+
/** The tip's coverage over `rect`, with the grain, quantised. */
|
|
1096
|
+
private coverage;
|
|
1097
|
+
private dab;
|
|
1098
|
+
}
|
|
1099
|
+
/** The wet settings each medium gives a layer the first time it holds wet paint. */
|
|
1100
|
+
declare const WET_MEDIUM_PARAMS: Readonly<Record<WetMediumKind, Partial<WetParams>>>;
|
|
1101
|
+
/** Settings of one wet layer (saved with the document). */
|
|
1102
|
+
interface WetLayerSettings {
|
|
1103
|
+
params: WetParams;
|
|
1104
|
+
flow: WetFlowSpec;
|
|
1105
|
+
}
|
|
1106
|
+
/** One wet layer as tools describe it. */
|
|
1107
|
+
interface WetLayerInfo {
|
|
1108
|
+
layerId: string;
|
|
1109
|
+
/** Surface water anywhere on the layer. */
|
|
1110
|
+
wet: boolean;
|
|
1111
|
+
/** Cells with surface water. */
|
|
1112
|
+
wetCells: number;
|
|
1113
|
+
/** Where the surface water is, or `null`. */
|
|
1114
|
+
wetBounds: RasterRect | null;
|
|
1115
|
+
/** 256 px tiles being simulated. */
|
|
1116
|
+
activeTiles: number;
|
|
1117
|
+
params: WetParams;
|
|
1118
|
+
flow: WetFlowSpec;
|
|
1119
|
+
}
|
|
1120
|
+
/** An event recorded with the clock it happened at (strokes are recorded separately), for replays. */
|
|
1121
|
+
interface WetEvent {
|
|
1122
|
+
clock: number;
|
|
1123
|
+
/** How many strokes were recorded before it (where it falls between strokes in a replay). */
|
|
1124
|
+
at?: number;
|
|
1125
|
+
kind: 'dry' | 'tilt' | 'params' | 'flow' | 'wet-paper';
|
|
1126
|
+
layerId?: string;
|
|
1127
|
+
data?: unknown;
|
|
1128
|
+
}
|
|
1129
|
+
interface PaintWetHost {
|
|
1130
|
+
canvas(): {
|
|
1131
|
+
width: number;
|
|
1132
|
+
height: number;
|
|
1133
|
+
};
|
|
1134
|
+
paper(): PaperSettings;
|
|
1135
|
+
/** The layer's current pixels (`null` when it is not a pixel layer). */
|
|
1136
|
+
layerSurface(id: string): TiledSurface | null;
|
|
1137
|
+
/** Shows new pixels for wet layers (not an undo step); `rects` are where they changed. */
|
|
1138
|
+
updateSurfaces(surfaces: ReadonlyMap<string, TiledSurface>, rects: RasterRect[]): void;
|
|
1139
|
+
/** The paint dried while time passed (the editor records nothing, it just redraws). */
|
|
1140
|
+
changed(): void;
|
|
1141
|
+
}
|
|
1142
|
+
interface PaintWetOptions extends WetWorldOptions {
|
|
1143
|
+
/** Steps per second while time runs (default 120). */
|
|
1144
|
+
speed?: number;
|
|
1145
|
+
/** Bytes of wet snapshots kept for undo (default 512 MB). */
|
|
1146
|
+
undoMemory?: number;
|
|
1147
|
+
}
|
|
1148
|
+
/** The paint editor's wet layers: one `WetWorld` per canvas, its settings and time. */
|
|
1149
|
+
declare class PaintWetLayers {
|
|
1150
|
+
private readonly host;
|
|
1151
|
+
private readonly options;
|
|
1152
|
+
world: WetWorld | null;
|
|
1153
|
+
tilt: WetTilt;
|
|
1154
|
+
readonly settings: Map<string, WetLayerSettings>;
|
|
1155
|
+
paused: boolean;
|
|
1156
|
+
/** Wet strokes dry as soon as they end (one undo step with the stroke). */
|
|
1157
|
+
instantDry: boolean;
|
|
1158
|
+
speed: number;
|
|
1159
|
+
overlay: boolean;
|
|
1160
|
+
readonly undoMemory: number;
|
|
1161
|
+
/** The live wet stroke, if any. */
|
|
1162
|
+
driver: WetStrokeDriver | null;
|
|
1163
|
+
private lastTick;
|
|
1164
|
+
private ticking;
|
|
1165
|
+
private stepsOwed;
|
|
1166
|
+
constructor(host: PaintWetHost, options?: PaintWetOptions);
|
|
1167
|
+
/** The world (made on first use, at the canvas size). */
|
|
1168
|
+
ensureWorld(): WetWorld;
|
|
1169
|
+
/** Whether any layer (or `layerId`) has wet paint. */
|
|
1170
|
+
isWet(layerId?: string): boolean;
|
|
1171
|
+
/** Layers holding a wet layer (wet or drying). */
|
|
1172
|
+
wetLayerIds(): string[];
|
|
1173
|
+
/** Whether a batch is running (edits wait). */
|
|
1174
|
+
get busy(): boolean;
|
|
1175
|
+
/** Resolves when no batch is running. */
|
|
1176
|
+
idle(): Promise<void>;
|
|
1177
|
+
settingsOf(layerId: string, medium?: WetMediumKind): WetLayerSettings;
|
|
1178
|
+
/** Puts a layer in the world (its current pixels become the base under the wet paint). */
|
|
1179
|
+
ensureLane(layerId: string, medium?: WetMediumKind): WetWorld;
|
|
1180
|
+
setParams(layerId: string, update: Partial<WetParams>): WetParams;
|
|
1181
|
+
setFlow(layerId: string, update: Partial<WetFlowSpec>): WetFlowSpec;
|
|
1182
|
+
setTilt(update: Partial<WetTilt>): WetTilt;
|
|
1183
|
+
/** The paper changed. */
|
|
1184
|
+
paperChanged(): void;
|
|
1185
|
+
/** A layer as tools describe it. */
|
|
1186
|
+
info(layerId: string): WetLayerInfo;
|
|
1187
|
+
backend(): WetBackendInfo | null;
|
|
1188
|
+
/** Shows the current wet pixels (at most `maxTiles` reshaded per layer). */
|
|
1189
|
+
syncDisplays(maxTiles?: number): RasterRect[];
|
|
1190
|
+
/** Drops the wet layers whose paint has all dried (they become ordinary pixel layers). */
|
|
1191
|
+
private retireDry;
|
|
1192
|
+
/**
|
|
1193
|
+
* Advances time by the wall clock (`now` in ms, e.g. `performance.now()`): `speed` steps per
|
|
1194
|
+
* second, at most `maxSteps` per call. Call it every frame; it does nothing while paused or dry.
|
|
1195
|
+
* Resolves when the steps are done and the display is up to date.
|
|
1196
|
+
*/
|
|
1197
|
+
tick(now: number, maxSteps?: number): Promise<void>;
|
|
1198
|
+
private flushDriver;
|
|
1199
|
+
/** Called when a live stroke that ended mid-batch has finished applying. */
|
|
1200
|
+
onDriverDone: ((driver: WetStrokeDriver) => void) | null;
|
|
1201
|
+
/** Steps `steps` times now (agents, tests). Returns the steps run. */
|
|
1202
|
+
advance(steps: number): Promise<number>;
|
|
1203
|
+
/**
|
|
1204
|
+
* Dries at once (everything settles where it is), on one layer or all, optionally only in a
|
|
1205
|
+
* rectangle. Returns the layers and rectangles that dried.
|
|
1206
|
+
*/
|
|
1207
|
+
dry(layerId?: string, rect?: RasterRect): Array<{
|
|
1208
|
+
layerId: string;
|
|
1209
|
+
bounds: RasterRect | null;
|
|
1210
|
+
}>;
|
|
1211
|
+
/** The layer's pixels with all its wet paint dried (deterministic), without changing anything. */
|
|
1212
|
+
bakedSurface(layerId: string): TiledSurface | null;
|
|
1213
|
+
/** A snapshot of the world for undo (`null` when there is none). */
|
|
1214
|
+
snapshot(): WetSnapshot | null;
|
|
1215
|
+
/** Puts a snapshot back (`null`: no wet layers). Returns the layer surfaces it implies. */
|
|
1216
|
+
restore(snap: WetSnapshot | null): void;
|
|
1217
|
+
/** Forgets everything wet (a new canvas, a resize, opening a document). */
|
|
1218
|
+
reset(): void;
|
|
1219
|
+
/** Water overlay tiles (RGBA) over a document rectangle, for the view. */
|
|
1220
|
+
waterOverlay(rect: RasterRect): Array<{
|
|
1221
|
+
tx: number;
|
|
1222
|
+
ty: number;
|
|
1223
|
+
data: Uint8ClampedArray;
|
|
1224
|
+
}>;
|
|
1225
|
+
destroy(): void;
|
|
1226
|
+
}
|
|
1227
|
+
/** The pigment (K r, g, b, S) of a colour at a body, per unit mass (for tools that report it). */
|
|
1228
|
+
declare function wetPigmentOf(hex: string, body: number): number[];
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* Thick paint (impasto): a pixel layer can carry two extra channels, a **height** map (how far the
|
|
1232
|
+
* paint stands up, 0..255) and a **material** map (R gloss, G metallic, 0..255), and it is lit at
|
|
1233
|
+
* render time: a normal from the height's slope, diffuse light from `PaintLighting`, and a
|
|
1234
|
+
* Blinn-Phong highlight whose sharpness follows the gloss, tinted by the paint where it is
|
|
1235
|
+
* metallic. The layer's own pixels stay the paint's colour (albedo), so relighting never repaints
|
|
1236
|
+
* anything, saving keeps colour, height and material apart, and only tiles whose inputs changed are
|
|
1237
|
+
* relit (the lit tiles are cached by the identity of their input tiles).
|
|
1238
|
+
*
|
|
1239
|
+
* Impasto strokes (`ThickStrokeDriver`) lay colour through the shared pick-up / mix exchange (paint
|
|
1240
|
+
* mixes into the wet paint it meets, like oil) and raise the height: `brush` with bristle ridges
|
|
1241
|
+
* along the stroke, `tube` in domed blobs, `knife` scraping the relief flat while it smears the
|
|
1242
|
+
* paint, `roller` as a flat stippled band, `gloop` as a thick glossy round bead. The paint runs out
|
|
1243
|
+
* along the stroke with `load` below 1, and `thinner` flattens and thins it.
|
|
1244
|
+
*/
|
|
1245
|
+
|
|
1246
|
+
/** Where the light comes from. */
|
|
1247
|
+
interface PaintLighting {
|
|
1248
|
+
/** Degrees clockwise from the top of the canvas (315: top left, the default). */
|
|
1249
|
+
azimuth: number;
|
|
1250
|
+
/** Degrees above the canvas, 5 (grazing, deep shadows) .. 90 (straight on, no relief). Default 40. */
|
|
1251
|
+
elevation: number;
|
|
1252
|
+
/** How strongly the relief shows, 0 (flat) .. 1 (default 0.7). */
|
|
1253
|
+
relief: number;
|
|
1254
|
+
}
|
|
1255
|
+
declare const DEFAULT_LIGHTING: Readonly<PaintLighting>;
|
|
1256
|
+
declare function normalizeLighting(raw?: Partial<PaintLighting>, base?: PaintLighting): PaintLighting;
|
|
1257
|
+
/** A layer's thick-paint channels (layer space, like its pixels). */
|
|
1258
|
+
interface ThickChannels {
|
|
1259
|
+
/** Height 0..255 per pixel (1 channel). */
|
|
1260
|
+
height: TiledSurface;
|
|
1261
|
+
/** Material per pixel (4 channels): R gloss, G metallic, B and A unused. */
|
|
1262
|
+
material: TiledSurface;
|
|
1263
|
+
}
|
|
1264
|
+
declare function emptyThickChannels(): ThickChannels;
|
|
1265
|
+
declare function lightingKey(l: PaintLighting): string;
|
|
1266
|
+
/**
|
|
1267
|
+
* One tile of a thick layer lit: `color` (RGBA, straight), the height and material of the tile, and
|
|
1268
|
+
* the height of its 8 neighbours for the slopes at the border (`heightAt(dx, dy)` returns the
|
|
1269
|
+
* neighbouring tile's data or null). Returns a new RGBA tile.
|
|
1270
|
+
*/
|
|
1271
|
+
declare function lightTile(color: Uint8ClampedArray, height: Uint8ClampedArray | null, heightAround: (dx: number, dy: number) => Uint8ClampedArray | null, material: Uint8ClampedArray | null, light: PaintLighting): Uint8ClampedArray;
|
|
1272
|
+
/** Cache of lit tiles for one layer, by the identity of their inputs. */
|
|
1273
|
+
declare class LitCache {
|
|
1274
|
+
private readonly tiles;
|
|
1275
|
+
/** The layer's pixels lit (tiles without relief nearby are the colour tiles themselves). */
|
|
1276
|
+
lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting): TiledSurface;
|
|
1277
|
+
}
|
|
1278
|
+
interface ThickStrokeInit {
|
|
1279
|
+
brush: PaintBrush;
|
|
1280
|
+
/** Where the colour goes (the session's live tiles). */
|
|
1281
|
+
target: PaintTileTarget;
|
|
1282
|
+
/** The layer's channels before the stroke. */
|
|
1283
|
+
channels: ThickChannels;
|
|
1284
|
+
/** Paint colour, sRGB 0..1. */
|
|
1285
|
+
color: RgbTriple;
|
|
1286
|
+
load: MixerLoad;
|
|
1287
|
+
seed: number;
|
|
1288
|
+
copies: readonly StrokeCopy[];
|
|
1289
|
+
}
|
|
1290
|
+
/** One impasto stroke: colour through the exchange, height and material into live channel tiles. */
|
|
1291
|
+
declare class ThickStrokeDriver {
|
|
1292
|
+
readonly init: ThickStrokeInit;
|
|
1293
|
+
dabCount: number;
|
|
1294
|
+
private readonly smoother;
|
|
1295
|
+
private readonly states;
|
|
1296
|
+
private readonly medium;
|
|
1297
|
+
private readonly height;
|
|
1298
|
+
private readonly material;
|
|
1299
|
+
private readonly paint;
|
|
1300
|
+
private tip;
|
|
1301
|
+
private lastRaw;
|
|
1302
|
+
private lastFed;
|
|
1303
|
+
private version;
|
|
1304
|
+
constructor(init: ThickStrokeInit);
|
|
1305
|
+
private readonly copies;
|
|
1306
|
+
/** What copy 0 carries after the dabs so far. */
|
|
1307
|
+
get load(): MixerLoad;
|
|
1308
|
+
/** The channels with the stroke so far (new objects whenever they changed). */
|
|
1309
|
+
channels(): ThickChannels;
|
|
1310
|
+
private heightTile;
|
|
1311
|
+
private materialTile;
|
|
1312
|
+
private getH;
|
|
1313
|
+
private setH;
|
|
1314
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
1315
|
+
finish(): RasterRect[];
|
|
1316
|
+
private applyDabs;
|
|
1317
|
+
private dab;
|
|
1318
|
+
/** The knife: flattens the relief under it and carries some of it along (a patch, like a smudge). */
|
|
1319
|
+
private knifeHeight;
|
|
1320
|
+
private paintMaterial;
|
|
1321
|
+
}
|
|
1322
|
+
/** Height statistics of a layer's relief (for tools). */
|
|
1323
|
+
declare function reliefInfo(ch: ThickChannels): {
|
|
1324
|
+
bounds: RasterRect | null;
|
|
1325
|
+
maxHeight: number;
|
|
1326
|
+
tiles: number;
|
|
1327
|
+
};
|
|
1328
|
+
/** A layer's thick-paint channels as saved: PNG data URLs over a rectangle (layer space). */
|
|
1329
|
+
interface SavedThickChannels {
|
|
1330
|
+
rect: RasterRect;
|
|
1331
|
+
/** Greyscale PNG of the height. */
|
|
1332
|
+
height: string;
|
|
1333
|
+
/** RGBA PNG of the material (R gloss, G metallic). */
|
|
1334
|
+
material: string;
|
|
1335
|
+
}
|
|
1336
|
+
/** Channels → saved form (`null` when there is no relief). */
|
|
1337
|
+
declare function thickToJSON(ch: ThickChannels): SavedThickChannels | null;
|
|
1338
|
+
/** Saved form → channels; throws a sentence when it cannot be read. */
|
|
1339
|
+
declare function thickFromJSON(raw: unknown): ThickChannels;
|
|
506
1340
|
|
|
507
1341
|
/**
|
|
508
1342
|
* The paint editor's document: a canvas (size and resolution), a paper, and the shared raster
|
|
@@ -537,6 +1371,22 @@ interface PaintDocumentState {
|
|
|
537
1371
|
activeLayerId: string | null;
|
|
538
1372
|
/** Every committed stroke, oldest first (strokes are data: replay, vary, time-lapse later). */
|
|
539
1373
|
strokes: RecordedStroke[];
|
|
1374
|
+
/** Wet-media events other than strokes (drying, tilt, settings), with the clock they happened at. */
|
|
1375
|
+
wetEvents?: WetEvent[];
|
|
1376
|
+
/** Changes besides strokes that a replay repeats (fills, clears, flattened relief), in order. */
|
|
1377
|
+
paintEvents?: PaintEvent[];
|
|
1378
|
+
/** Sticker layers: each layer's stickers, bottom first (its pixels are drawn from them). */
|
|
1379
|
+
stickers?: Readonly<Record<string, Sticker[]>>;
|
|
1380
|
+
/** Thick paint: each layer's height and material channels (layers without relief are missing). */
|
|
1381
|
+
thick?: Readonly<Record<string, ThickChannels>>;
|
|
1382
|
+
}
|
|
1383
|
+
/** A change a replay repeats besides strokes, recorded with how many strokes came before it. */
|
|
1384
|
+
interface PaintEvent {
|
|
1385
|
+
at: number;
|
|
1386
|
+
kind: 'fill' | 'clear' | 'flatten' | 'stickers';
|
|
1387
|
+
layerId: string;
|
|
1388
|
+
/** The fill's options (colour, opacity, grain, pressure, blend), or the layer's stickers after an edit. */
|
|
1389
|
+
data?: Record<string, unknown>;
|
|
540
1390
|
}
|
|
541
1391
|
/** A new layer id, unique in this session. */
|
|
542
1392
|
declare function newPaintLayerId(): string;
|
|
@@ -627,18 +1477,6 @@ declare class PaintDabStroke {
|
|
|
627
1477
|
private depositWithCeiling;
|
|
628
1478
|
}
|
|
629
1479
|
|
|
630
|
-
/**
|
|
631
|
-
* A live stroke on one pixel layer. Points arrive while the pen moves; the layer shows the stroke
|
|
632
|
-
* so far through a live surface (no history); `end()` commits it as one undo step.
|
|
633
|
-
*
|
|
634
|
-
* - Dab and eraser brushes: the coverage is cumulative, so each update recomposites the touched
|
|
635
|
-
* region from the pre-stroke tile (`applyStrokeToTile` with `into` and `region`).
|
|
636
|
-
* - Dab-by-dab brushes (`PaintDabStroke`: the Mixer Brush, blender, smudge, and dab brushes that
|
|
637
|
-
* pick up paint or are multi-coloured): dabs read what earlier dabs laid down, so they paint
|
|
638
|
-
* into live tile copies; every tile written since the last update gets a new `Tile` object,
|
|
639
|
-
* because the compositor caches by tile identity.
|
|
640
|
-
*/
|
|
641
|
-
|
|
642
1480
|
interface StrokeSessionHost {
|
|
643
1481
|
/** Shows the layer's live surface (no history); `rects` are where it changed. */
|
|
644
1482
|
update(layerId: string, surface: TiledSurface, rects: RasterRect[]): void;
|
|
@@ -672,6 +1510,28 @@ interface StrokeSessionInit {
|
|
|
672
1510
|
mixerLoad: MixerLoad;
|
|
673
1511
|
/** The secondary colour 0..255 RGBA (a multi-colour brush without its own colours uses it). */
|
|
674
1512
|
secondary?: readonly [number, number, number, number];
|
|
1513
|
+
/**
|
|
1514
|
+
* Wet brushes: the driver that applies the stroke to the wet layer, and the layer's current
|
|
1515
|
+
* pixels (the wet layer's display). The session's own surface is not used.
|
|
1516
|
+
*/
|
|
1517
|
+
wet?: {
|
|
1518
|
+
driver: WetStrokeDriver;
|
|
1519
|
+
surface: () => TiledSurface;
|
|
1520
|
+
};
|
|
1521
|
+
/** Impasto brushes: the layer's thick-paint channels before the stroke. */
|
|
1522
|
+
thick?: {
|
|
1523
|
+
channels: ThickChannels;
|
|
1524
|
+
};
|
|
1525
|
+
/** Where paint may land (canvas-sized coverage 0..255: the stencils' mask), or none. */
|
|
1526
|
+
selection?: {
|
|
1527
|
+
width: number;
|
|
1528
|
+
height: number;
|
|
1529
|
+
data: ArrayLike<number>;
|
|
1530
|
+
} | null;
|
|
1531
|
+
/** Sticker brushes: the next sticker number on the layer. */
|
|
1532
|
+
sticker?: {
|
|
1533
|
+
nextId: number;
|
|
1534
|
+
};
|
|
675
1535
|
}
|
|
676
1536
|
interface PaintStrokeResult {
|
|
677
1537
|
layerId: string;
|
|
@@ -682,6 +1542,8 @@ interface PaintStrokeResult {
|
|
|
682
1542
|
strokeId: string | null;
|
|
683
1543
|
/** Mixer and wet-capable dab brushes: what the brush carries after the stroke (cleaned unless it is dirty). */
|
|
684
1544
|
mixerLoad?: MixerLoad;
|
|
1545
|
+
/** Wet brushes: the paint is wet (it keeps moving until it dries; see dry_paint / advance_wet_paint). */
|
|
1546
|
+
wet?: boolean;
|
|
685
1547
|
}
|
|
686
1548
|
declare class PaintStrokeSession {
|
|
687
1549
|
private readonly host;
|
|
@@ -695,17 +1557,35 @@ declare class PaintStrokeSession {
|
|
|
695
1557
|
private readonly fresh;
|
|
696
1558
|
private readonly dry;
|
|
697
1559
|
private readonly wet;
|
|
1560
|
+
private readonly wetDriver;
|
|
1561
|
+
private readonly thickDriver;
|
|
1562
|
+
private readonly stickerSpray;
|
|
1563
|
+
/** A wet stroke that ended while the simulation was mid-batch: it commits when the driver catches up. */
|
|
1564
|
+
private wetEndPending;
|
|
698
1565
|
private readonly apply;
|
|
699
1566
|
private readonly copies;
|
|
700
1567
|
private bounds;
|
|
701
1568
|
private lastRaw;
|
|
702
1569
|
private closed;
|
|
703
1570
|
result: PaintStrokeResult | null;
|
|
1571
|
+
/** The mask stencils this stroke was painted with when they are not the editor's (replays). */
|
|
1572
|
+
maskStencils: readonly Stencil[] | null;
|
|
704
1573
|
constructor(host: StrokeSessionHost, init: StrokeSessionInit);
|
|
705
1574
|
private tileTarget;
|
|
706
1575
|
get open(): boolean;
|
|
707
1576
|
/** Adds raw pen samples (document pixels). */
|
|
708
1577
|
push(points: readonly PenPoint[]): void;
|
|
1578
|
+
/** Sticker strokes: the stickers sprayed so far. */
|
|
1579
|
+
stickers(): Sticker[] | null;
|
|
1580
|
+
/** Impasto strokes: the layer's thick-paint channels with the stroke so far. */
|
|
1581
|
+
thickChannels(): ThickChannels | null;
|
|
1582
|
+
/** Shows a wet stroke's progress (the wet layer's display). */
|
|
1583
|
+
wetUpdate(rects: RasterRect[]): void;
|
|
1584
|
+
/** Whether a wet stroke has ended but waits for the simulation to finish a batch. */
|
|
1585
|
+
get pendingEnd(): boolean;
|
|
1586
|
+
/** Completes a wet stroke whose end waited (the controller calls it when the driver is done). */
|
|
1587
|
+
completeWetEnd(): void;
|
|
1588
|
+
private finishWet;
|
|
709
1589
|
private update;
|
|
710
1590
|
/** Ends the stroke (one undo step) and returns what changed. */
|
|
711
1591
|
end(): PaintStrokeResult;
|
|
@@ -736,6 +1616,11 @@ interface PaperNode extends CompositorLayer {
|
|
|
736
1616
|
opacity: number;
|
|
737
1617
|
blend: 'normal';
|
|
738
1618
|
paper: PaperSettings;
|
|
1619
|
+
/** The light, for metallic paper. */
|
|
1620
|
+
light?: {
|
|
1621
|
+
azimuth: number;
|
|
1622
|
+
elevation: number;
|
|
1623
|
+
};
|
|
739
1624
|
}
|
|
740
1625
|
/** A view-only RGBA image under the paint (the tracing image), drawn at its opacity. */
|
|
741
1626
|
interface ImageNode extends CompositorLayer {
|
|
@@ -757,9 +1642,15 @@ interface ImageNode extends CompositorLayer {
|
|
|
757
1642
|
type ViewLayer = PaintLayer | PaperNode | ImageNode;
|
|
758
1643
|
declare const PAPER_NODE_ID = "__paper";
|
|
759
1644
|
declare const TRACING_NODE_ID = "__tracing";
|
|
760
|
-
declare function paperKey(p: PaperSettings
|
|
761
|
-
|
|
762
|
-
|
|
1645
|
+
declare function paperKey(p: PaperSettings, light?: {
|
|
1646
|
+
azimuth: number;
|
|
1647
|
+
elevation: number;
|
|
1648
|
+
}): string;
|
|
1649
|
+
/** The paper as a compositor layer (one object per paper settings object and light, so caches hold). */
|
|
1650
|
+
declare function paperNode(paper: PaperSettings, light?: {
|
|
1651
|
+
azimuth: number;
|
|
1652
|
+
elevation: number;
|
|
1653
|
+
}): PaperNode;
|
|
763
1654
|
/** The paper's dry height field, built once per settings. */
|
|
764
1655
|
declare function paperGrain(paper: PaperSettings): PaperGrain;
|
|
765
1656
|
/** A compositor for paint editor stacks (paper and view-only images included). */
|
|
@@ -788,7 +1679,7 @@ type PaintAssetStore = ImageEditorAssetStore;
|
|
|
788
1679
|
/**
|
|
789
1680
|
* Fields a later version adds. Phase 1 writes none of them and keeps any it reads. Planned:
|
|
790
1681
|
* `layerChannels: { [layerId]: { height?: asset, material?: asset } }` for impasto and
|
|
791
|
-
* metallic paint. Wet paint is never saved (saving dries it)
|
|
1682
|
+
* metallic paint. Wet paint itself is never saved (saving dries it); its settings are (`wet`).
|
|
792
1683
|
*/
|
|
793
1684
|
type PaintDocumentExtensions = Record<string, unknown>;
|
|
794
1685
|
interface SerializedPaintDocument {
|
|
@@ -820,6 +1711,8 @@ interface SerializeOptions {
|
|
|
820
1711
|
swatches?: unknown;
|
|
821
1712
|
/** The mixing pad: `{ current?: png data URL, saved: [{ id, name, png }] }`. */
|
|
822
1713
|
mixingPad?: unknown;
|
|
1714
|
+
/** Wet media settings: `{ tilt, layers: { [layerId]: { params, flow } } }`. */
|
|
1715
|
+
wet?: unknown;
|
|
823
1716
|
}
|
|
824
1717
|
/** The document as JSON with inline PNG data URLs. */
|
|
825
1718
|
declare function serializePaintDocument(state: PaintDocumentState, options?: SerializeOptions): SerializedPaintDocument;
|
|
@@ -833,6 +1726,8 @@ interface LoadedPaintDocument {
|
|
|
833
1726
|
swatches?: unknown;
|
|
834
1727
|
/** The saved mixing pad, as stored (the controller validates it). */
|
|
835
1728
|
mixingPad?: unknown;
|
|
1729
|
+
/** The saved wet settings, as stored (the controller validates them). */
|
|
1730
|
+
wet?: unknown;
|
|
836
1731
|
/** What could not be read (the rest still loads). */
|
|
837
1732
|
problems: string[];
|
|
838
1733
|
}
|
|
@@ -1016,126 +1911,6 @@ declare function renderBrushThumbnail(brush: PaintBrush, options: {
|
|
|
1016
1911
|
color: string;
|
|
1017
1912
|
}): BrushThumbnail;
|
|
1018
1913
|
|
|
1019
|
-
/**
|
|
1020
|
-
* Painting guides: mirror and kaleidoscope symmetry (every stroke paints its reflected or rotated
|
|
1021
|
-
* copies as one stroke), perspective guides with one to three vanishing points that strokes snap
|
|
1022
|
-
* to, a tracing image under the paint (optionally the source of the stroke colour), and floating
|
|
1023
|
-
* reference images to pick colours from. Geometry only: no pixels, no DOM.
|
|
1024
|
-
*/
|
|
1025
|
-
|
|
1026
|
-
type SymmetryMode = 'off' | 'vertical' | 'horizontal' | 'dual' | 'diagonal' | 'kaleidoscope';
|
|
1027
|
-
declare const SYMMETRY_MODES: readonly SymmetryMode[];
|
|
1028
|
-
interface SymmetryGuide {
|
|
1029
|
-
mode: SymmetryMode;
|
|
1030
|
-
/** Centre of the axes in document pixels (default the canvas centre). */
|
|
1031
|
-
center: {
|
|
1032
|
-
x: number;
|
|
1033
|
-
y: number;
|
|
1034
|
-
};
|
|
1035
|
-
/** Turns the axes, degrees clockwise (default 0). */
|
|
1036
|
-
rotation: number;
|
|
1037
|
-
/** Kaleidoscope: number of segments, 2..32 (default 6). */
|
|
1038
|
-
segments: number;
|
|
1039
|
-
/** Kaleidoscope: reflect every other segment as well as rotating (default true), like a real kaleidoscope. */
|
|
1040
|
-
mirror: boolean;
|
|
1041
|
-
}
|
|
1042
|
-
interface Point2 {
|
|
1043
|
-
x: number;
|
|
1044
|
-
y: number;
|
|
1045
|
-
}
|
|
1046
|
-
interface PerspectiveGuide {
|
|
1047
|
-
enabled: boolean;
|
|
1048
|
-
/** One to three vanishing points in document pixels (they may lie outside the canvas). */
|
|
1049
|
-
points: Point2[];
|
|
1050
|
-
/** Strokes snap to the guide lines (default true when enabled). */
|
|
1051
|
-
snap: boolean;
|
|
1052
|
-
}
|
|
1053
|
-
/** A view-only image under the paint, placed over the canvas. */
|
|
1054
|
-
interface TracingImage {
|
|
1055
|
-
/** PNG or JPEG data URL. */
|
|
1056
|
-
src: string;
|
|
1057
|
-
/** Natural size of the image in pixels. */
|
|
1058
|
-
width: number;
|
|
1059
|
-
height: number;
|
|
1060
|
-
/** Where it is drawn, in document pixels (default: fitted to the canvas). */
|
|
1061
|
-
rect: {
|
|
1062
|
-
x: number;
|
|
1063
|
-
y: number;
|
|
1064
|
-
width: number;
|
|
1065
|
-
height: number;
|
|
1066
|
-
};
|
|
1067
|
-
/** 0..1 (default 0.35). */
|
|
1068
|
-
opacity: number;
|
|
1069
|
-
visible: boolean;
|
|
1070
|
-
/** Strokes without an explicit colour take the tracing image's colour where they start. */
|
|
1071
|
-
sampleColors: boolean;
|
|
1072
|
-
}
|
|
1073
|
-
/** A floating image to look at and pick colours from (shown beside the canvas, never painted). */
|
|
1074
|
-
interface ReferenceImage {
|
|
1075
|
-
id: string;
|
|
1076
|
-
name: string;
|
|
1077
|
-
src: string;
|
|
1078
|
-
width: number;
|
|
1079
|
-
height: number;
|
|
1080
|
-
/** Where the panel floats over the viewport, in screen pixels, and its displayed width. */
|
|
1081
|
-
x: number;
|
|
1082
|
-
y: number;
|
|
1083
|
-
displayWidth: number;
|
|
1084
|
-
visible: boolean;
|
|
1085
|
-
}
|
|
1086
|
-
interface PaintGuides {
|
|
1087
|
-
symmetry: SymmetryGuide;
|
|
1088
|
-
perspective: PerspectiveGuide;
|
|
1089
|
-
tracing: TracingImage | null;
|
|
1090
|
-
references: ReferenceImage[];
|
|
1091
|
-
}
|
|
1092
|
-
declare function defaultGuides(canvas: {
|
|
1093
|
-
width: number;
|
|
1094
|
-
height: number;
|
|
1095
|
-
}): PaintGuides;
|
|
1096
|
-
declare const MAX_COORDINATE = 1000000;
|
|
1097
|
-
declare function normalizeSymmetry(raw: Partial<SymmetryGuide> | Record<string, unknown>, base: SymmetryGuide): SymmetryGuide;
|
|
1098
|
-
/** The axis directions of a mirror mode (radians), as photocraft's `directions`. */
|
|
1099
|
-
declare function symmetryAxisAngles(g: SymmetryGuide): number[];
|
|
1100
|
-
/**
|
|
1101
|
-
* The extra copies a stroke paints under the symmetry guide, as affine maps (the stroke itself is
|
|
1102
|
-
* not included). Vertical, horizontal and diagonal: one reflection. Dual: two reflections and the
|
|
1103
|
-
* point reflection. Kaleidoscope: rotations by 360°/n, and with `mirror` the reflections between them.
|
|
1104
|
-
*/
|
|
1105
|
-
declare function symmetryCopies(g: SymmetryGuide): Affine[];
|
|
1106
|
-
/** The axes clipped to the canvas, for drawing (photocraft's `segments`). */
|
|
1107
|
-
declare function symmetryAxisSegments(g: SymmetryGuide, size: {
|
|
1108
|
-
width: number;
|
|
1109
|
-
height: number;
|
|
1110
|
-
}): Array<[Point2, Point2]>;
|
|
1111
|
-
declare function normalizePerspective(raw: Partial<PerspectiveGuide> | Record<string, unknown>, base: PerspectiveGuide): PerspectiveGuide;
|
|
1112
|
-
/** The line families a perspective guide snaps to at `p`: unit directions. */
|
|
1113
|
-
declare function perspectiveDirections(g: PerspectiveGuide, p: Point2): Point2[];
|
|
1114
|
-
/** How far the pen must move before a stroke chooses its perspective line (document pixels). */
|
|
1115
|
-
declare const SNAP_DECIDE_DISTANCE = 6;
|
|
1116
|
-
/**
|
|
1117
|
-
* Snaps one stroke to a perspective guide: once the pen has moved `SNAP_DECIDE_DISTANCE` from
|
|
1118
|
-
* where it started, the stroke picks the guide line through its start that best matches its
|
|
1119
|
-
* direction, and every sample after that is projected onto the line.
|
|
1120
|
-
*/
|
|
1121
|
-
declare class PerspectiveSnapper {
|
|
1122
|
-
private readonly guide;
|
|
1123
|
-
private start;
|
|
1124
|
-
private dir;
|
|
1125
|
-
constructor(guide: PerspectiveGuide);
|
|
1126
|
-
/** The line the stroke follows, once chosen. */
|
|
1127
|
-
get line(): {
|
|
1128
|
-
from: Point2;
|
|
1129
|
-
direction: Point2;
|
|
1130
|
-
} | null;
|
|
1131
|
-
snap<P extends Point2>(p: P): P;
|
|
1132
|
-
}
|
|
1133
|
-
/** Guide lines to draw for a perspective guide: rays from each vanishing point through the canvas. */
|
|
1134
|
-
declare function perspectiveGuideLines(g: PerspectiveGuide, size: {
|
|
1135
|
-
width: number;
|
|
1136
|
-
height: number;
|
|
1137
|
-
}, count?: number): Array<[Point2, Point2]>;
|
|
1138
|
-
|
|
1139
1914
|
/**
|
|
1140
1915
|
* Colour for painters: hex / RGB / HSV / HSL conversions, a colour wheel that is either the
|
|
1141
1916
|
* painter's red-yellow-blue wheel (where red's complement is green) or the RGB wheel (where it is
|
|
@@ -1270,6 +2045,107 @@ declare class MixingPad {
|
|
|
1270
2045
|
pixels(): Uint8ClampedArray;
|
|
1271
2046
|
}
|
|
1272
2047
|
|
|
2048
|
+
/**
|
|
2049
|
+
* Replaying a painting from its data: every recorded stroke, and the changes recorded between them
|
|
2050
|
+
* (fills, clears, flattened relief, and wet-media events at the simulation clock they happened at),
|
|
2051
|
+
* repainted into a fresh, headless editor.
|
|
2052
|
+
*
|
|
2053
|
+
* - **Time-lapse**: frames of the painting as it is repainted (after strokes, and while wet paint
|
|
2054
|
+
* flows between them), as PNGs and as a `VideoProject` of stills that the video editor's export
|
|
2055
|
+
* path (`toVideoFfmpeg`) turns into an MP4.
|
|
2056
|
+
* - **High-resolution export**: the same replay at a larger scale (points, brush sizes, paper grain
|
|
2057
|
+
* and rectangles scaled), so the picture is painted again with more pixels instead of upscaled.
|
|
2058
|
+
*
|
|
2059
|
+
* Strokes paint exactly as recorded: same seed, load and symmetry copies, and wet strokes apply
|
|
2060
|
+
* each point at the clock it was recorded at, with the simulation stepped in between, so wet paint
|
|
2061
|
+
* flows as it did. What was not recorded as data (imported pixels, merges, transforms of layers,
|
|
2062
|
+
* changes made before strokes were recorded) is not repeated: those layers start empty.
|
|
2063
|
+
*/
|
|
2064
|
+
|
|
2065
|
+
interface PaintReplayOptions {
|
|
2066
|
+
/** Paint at this many times the size (1 for a time-lapse; 1..8 for a high-resolution export). */
|
|
2067
|
+
scale?: number;
|
|
2068
|
+
/** Stop after this many strokes (default all). */
|
|
2069
|
+
strokes?: number;
|
|
2070
|
+
}
|
|
2071
|
+
/** One frame of a replay. */
|
|
2072
|
+
interface ReplayFrame extends PixelImage {
|
|
2073
|
+
/** Strokes painted so far. */
|
|
2074
|
+
stroke: number;
|
|
2075
|
+
/** Wet-media clock (steps). */
|
|
2076
|
+
clock: number;
|
|
2077
|
+
}
|
|
2078
|
+
interface TimeLapseOptions {
|
|
2079
|
+
/** Longest side of each frame in pixels (default 720). */
|
|
2080
|
+
maxSide?: number;
|
|
2081
|
+
/** A frame after every this many strokes (default: about `frames` frames over the whole painting). */
|
|
2082
|
+
strokesPerFrame?: number;
|
|
2083
|
+
/** About how many frames in all (default 120). */
|
|
2084
|
+
frames?: number;
|
|
2085
|
+
/** While wet paint flows between strokes, a frame every this many steps (default 60; 0: none). */
|
|
2086
|
+
wetStepsPerFrame?: number;
|
|
2087
|
+
/** Frames per second of the video (default 12). */
|
|
2088
|
+
fps?: number;
|
|
2089
|
+
/** Hold the finished painting this long at the end (ms, default 1500). */
|
|
2090
|
+
holdMs?: number;
|
|
2091
|
+
/** Called after each frame with how far along the replay is (0..1). */
|
|
2092
|
+
onProgress?: (done: number) => void;
|
|
2093
|
+
}
|
|
2094
|
+
interface TimeLapse {
|
|
2095
|
+
fps: number;
|
|
2096
|
+
width: number;
|
|
2097
|
+
height: number;
|
|
2098
|
+
/** Each frame as PNG bytes. */
|
|
2099
|
+
frames: Uint8Array[];
|
|
2100
|
+
/** The frames as stills on a timeline (assets `frame-0001.png`…; `src` is that file name). */
|
|
2101
|
+
video: VideoProject;
|
|
2102
|
+
/** The ffmpeg command that renders `video` to `timelapse.mp4` from the frame files. */
|
|
2103
|
+
ffmpeg: VideoFfmpegExport;
|
|
2104
|
+
durationMs: number;
|
|
2105
|
+
}
|
|
2106
|
+
type Item = {
|
|
2107
|
+
kind: 'stroke';
|
|
2108
|
+
stroke: RecordedStroke;
|
|
2109
|
+
} | {
|
|
2110
|
+
kind: 'wet';
|
|
2111
|
+
event: WetEvent;
|
|
2112
|
+
} | {
|
|
2113
|
+
kind: 'paint';
|
|
2114
|
+
event: NonNullable<PaintDocumentState['paintEvents']>[number];
|
|
2115
|
+
};
|
|
2116
|
+
/** Strokes and events in the order they happened. */
|
|
2117
|
+
declare function replayItems(doc: PaintDocumentState): Item[];
|
|
2118
|
+
/** The layer tree with every pixel layer empty (what a replay paints into). */
|
|
2119
|
+
declare function emptiedLayers(layers: readonly PaintLayer[]): PaintLayer[];
|
|
2120
|
+
/**
|
|
2121
|
+
* Replays a controller's painting into `target` (a fresh controller the caller made at the scaled
|
|
2122
|
+
* size; see `PaintEditorController.createReplayTarget`), calling `onFrame` where frames are due.
|
|
2123
|
+
*/
|
|
2124
|
+
declare function replayPainting(source: PaintDocumentState, target: PaintEditorController, options?: PaintReplayOptions & {
|
|
2125
|
+
onStroke?: (index: number) => Promise<void> | void;
|
|
2126
|
+
onWetSteps?: (clock: number) => Promise<void> | void;
|
|
2127
|
+
wetChunk?: number;
|
|
2128
|
+
}): Promise<void>;
|
|
2129
|
+
/** A frame of `target` at most `maxSide` pixels on its longest side. */
|
|
2130
|
+
declare function replayFrame(target: PaintEditorController, maxSide: number, stroke: number): ReplayFrame;
|
|
2131
|
+
/** Frames as stills on a video timeline, `1 / fps` each, with the last held for `holdMs`. */
|
|
2132
|
+
declare function timeLapseVideo(frameCount: number, width: number, height: number, fps: number, holdMs: number, name?: string): {
|
|
2133
|
+
video: VideoProject;
|
|
2134
|
+
ffmpeg: VideoFfmpegExport;
|
|
2135
|
+
durationMs: number;
|
|
2136
|
+
};
|
|
2137
|
+
/** Builds the time-lapse of a painting (see `PaintEditorController.timeLapse`). */
|
|
2138
|
+
declare function buildTimeLapse(source: PaintDocumentState, target: PaintEditorController, options?: TimeLapseOptions): Promise<TimeLapse>;
|
|
2139
|
+
/** A contact sheet: up to `count` frames side by side, for a quick look. */
|
|
2140
|
+
declare function contactSheet(frames: readonly PixelImage[], columns?: number, cell?: number): PixelImage;
|
|
2141
|
+
|
|
2142
|
+
/**
|
|
2143
|
+
* A time-lapse as one ZIP: the frames (`frame-0001.png`…), the video project (`timelapse.json`,
|
|
2144
|
+
* which the video editor opens), and `render.sh` with the ffmpeg command that turns them into
|
|
2145
|
+
* `timelapse.mp4`.
|
|
2146
|
+
*/
|
|
2147
|
+
declare function timeLapseZip(tl: TimeLapse): Promise<Uint8Array>;
|
|
2148
|
+
|
|
1273
2149
|
/**
|
|
1274
2150
|
* `PaintEditorController`: the natural-media paint editor's state, framework-free.
|
|
1275
2151
|
*
|
|
@@ -1301,6 +2177,26 @@ interface PaintEditorOptions extends Partial<PaintCanvas> {
|
|
|
1301
2177
|
encodeImage?: ImageEncoder;
|
|
1302
2178
|
/** Undo steps kept (default 200). */
|
|
1303
2179
|
historyLimit?: number;
|
|
2180
|
+
/** Wet media: kernel, threads, speed (steps per second) and the memory kept for undoing wet strokes. */
|
|
2181
|
+
wet?: PaintWetOptions;
|
|
2182
|
+
}
|
|
2183
|
+
/** Wet media as panels show it. */
|
|
2184
|
+
interface PaintWetStateInfo {
|
|
2185
|
+
/** Layers with a wet layer (paint still wet or drying). */
|
|
2186
|
+
layerIds: string[];
|
|
2187
|
+
/** Some paint is wet. */
|
|
2188
|
+
wet: boolean;
|
|
2189
|
+
/** Time is stopped (the paint stays wet). */
|
|
2190
|
+
paused: boolean;
|
|
2191
|
+
/** The water overlay is shown. */
|
|
2192
|
+
overlay: boolean;
|
|
2193
|
+
/** Wet strokes dry as soon as they end. */
|
|
2194
|
+
instantDry: boolean;
|
|
2195
|
+
/** Steps per second while time runs. */
|
|
2196
|
+
speed: number;
|
|
2197
|
+
/** Simulation steps so far. */
|
|
2198
|
+
clock: number;
|
|
2199
|
+
tilt: WetTilt;
|
|
1304
2200
|
}
|
|
1305
2201
|
interface PaintEditorState {
|
|
1306
2202
|
document: PaintDocumentState;
|
|
@@ -1330,6 +2226,8 @@ interface PaintEditorState {
|
|
|
1330
2226
|
savedMixingPads: readonly SavedMixingPadInfo[];
|
|
1331
2227
|
/** A stroke is in progress. */
|
|
1332
2228
|
stroking: boolean;
|
|
2229
|
+
/** Wet media. */
|
|
2230
|
+
wet: PaintWetStateInfo;
|
|
1333
2231
|
/** Increments on every change. */
|
|
1334
2232
|
version: number;
|
|
1335
2233
|
}
|
|
@@ -1371,6 +2269,10 @@ interface PaintStrokeInit {
|
|
|
1371
2269
|
};
|
|
1372
2270
|
/** What the brush carries at the start (default: from the editor's brush load); replays pass the recorded one. */
|
|
1373
2271
|
load?: MixerLoad;
|
|
2272
|
+
/** Follow guide stencils and keep to mask stencils (default true; replays pass false, their points already follow them). */
|
|
2273
|
+
stencils?: boolean;
|
|
2274
|
+
/** Mask with these stencils instead (replays pass the ones recorded with the stroke). */
|
|
2275
|
+
maskStencils?: readonly Stencil[];
|
|
1374
2276
|
}
|
|
1375
2277
|
interface LayerUpdate {
|
|
1376
2278
|
name?: string;
|
|
@@ -1409,6 +2311,12 @@ interface RenderOptions {
|
|
|
1409
2311
|
paper?: boolean;
|
|
1410
2312
|
/** Only these layers (a group with its children). */
|
|
1411
2313
|
layerIds?: readonly string[];
|
|
2314
|
+
/**
|
|
2315
|
+
* Wet paint as it is now (`display`: still wet, wet paper a little darker) or dried where it is
|
|
2316
|
+
* (`dried`, the deterministic bake that saving uses). Default `dried` for exports and `display`
|
|
2317
|
+
* for previews and picks.
|
|
2318
|
+
*/
|
|
2319
|
+
wet?: 'display' | 'dried';
|
|
1412
2320
|
}
|
|
1413
2321
|
interface PixelImage {
|
|
1414
2322
|
width: number;
|
|
@@ -1456,6 +2364,16 @@ declare class PaintEditorController {
|
|
|
1456
2364
|
private nextReferenceId;
|
|
1457
2365
|
private readonly viewCompositor;
|
|
1458
2366
|
private readonly exportCompositor;
|
|
2367
|
+
/** Wet media: the simulation, its settings and time. */
|
|
2368
|
+
readonly wet: PaintWetLayers;
|
|
2369
|
+
private readonly wetOptions;
|
|
2370
|
+
/** Where the light comes from for thick paint (saved with the document, not undo steps). */
|
|
2371
|
+
private lighting;
|
|
2372
|
+
private readonly litCaches;
|
|
2373
|
+
/** The live impasto stroke's channels. */
|
|
2374
|
+
private liveThick;
|
|
2375
|
+
/** Set while an operation that changes wet paint runs: the simulation before it (for undo). */
|
|
2376
|
+
private wetOp;
|
|
1459
2377
|
constructor(options?: PaintEditorOptions);
|
|
1460
2378
|
subscribe(listener: () => void): () => void;
|
|
1461
2379
|
private emit;
|
|
@@ -1469,6 +2387,24 @@ declare class PaintEditorController {
|
|
|
1469
2387
|
takeDirty(): RasterRect[] | 'all';
|
|
1470
2388
|
private requireIdle;
|
|
1471
2389
|
private commit;
|
|
2390
|
+
/** Keeps the wet snapshots in history within the wet undo memory, dropping the oldest. */
|
|
2391
|
+
private trimWetHistory;
|
|
2392
|
+
/** Throws when wet paint is mid-step (a batch in worker threads); `wetIdle()` waits for it. */
|
|
2393
|
+
private requireWetIdle;
|
|
2394
|
+
/** Starts an operation that changes wet paint: keeps the simulation as it is now for undo. */
|
|
2395
|
+
private beginWetOp;
|
|
2396
|
+
/**
|
|
2397
|
+
* Before a non-wet change to layers that hold wet paint (a dry brush, a fill, a merge…): dries
|
|
2398
|
+
* them first, in the same undo step. Returns the ids dried.
|
|
2399
|
+
*/
|
|
2400
|
+
private dryLayersFirst;
|
|
2401
|
+
/** Records a wet event (for replays) in the document being built. */
|
|
2402
|
+
private pendingWetEvents;
|
|
2403
|
+
private logWet;
|
|
2404
|
+
/** The document with a paint event appended (for replays). */
|
|
2405
|
+
private withPaintEvent;
|
|
2406
|
+
/** The document with the pending wet events appended. */
|
|
2407
|
+
private withWetEvents;
|
|
1472
2408
|
undo(): string | null;
|
|
1473
2409
|
redo(): string | null;
|
|
1474
2410
|
/** Labels of the undo steps, oldest first. */
|
|
@@ -1621,6 +2557,134 @@ declare class PaintEditorController {
|
|
|
1621
2557
|
bounds: RasterRect | null;
|
|
1622
2558
|
layerIds: string[];
|
|
1623
2559
|
};
|
|
2560
|
+
/** The document's `thick` without these layers (as a spread: `{}` when unchanged). */
|
|
2561
|
+
private thickWithout;
|
|
2562
|
+
/** Where the light comes from for thick paint and metallic paper. */
|
|
2563
|
+
getLighting(): PaintLighting;
|
|
2564
|
+
/** Moves the light (thick paint and metallic paper are relit; nothing is repainted). Saved with the document, not an undo step. */
|
|
2565
|
+
setLighting(update: Partial<PaintLighting>): PaintLighting;
|
|
2566
|
+
/** A layer's relief: where it is, its highest point (0..255) and its tiles; `null` when it has none. */
|
|
2567
|
+
reliefOf(layerId?: string): {
|
|
2568
|
+
layerId: string;
|
|
2569
|
+
bounds: RasterRect | null;
|
|
2570
|
+
maxHeight: number;
|
|
2571
|
+
} | null;
|
|
2572
|
+
/** Flattens a layer's thick paint (its colour stays; the relief and material go). One undo step. */
|
|
2573
|
+
flattenRelief(layerId?: string): {
|
|
2574
|
+
layerId: string;
|
|
2575
|
+
flattened: boolean;
|
|
2576
|
+
};
|
|
2577
|
+
/**
|
|
2578
|
+
* A fresh, headless editor for replaying this painting at `scale` (canvas, dpi and paper grain
|
|
2579
|
+
* scaled; every pixel layer empty; the same brushes, lighting and wet options). Dispose of it
|
|
2580
|
+
* when done.
|
|
2581
|
+
*/
|
|
2582
|
+
createReplayTarget(scale?: number): PaintEditorController;
|
|
2583
|
+
/**
|
|
2584
|
+
* Repaints the painting from its recorded strokes and events, as a time-lapse: PNG frames (after
|
|
2585
|
+
* strokes, and while wet paint flows), a `VideoProject` of the frames as stills and the ffmpeg
|
|
2586
|
+
* command that renders it (the video editor's export path). Changes nothing here.
|
|
2587
|
+
*/
|
|
2588
|
+
timeLapse(options?: TimeLapseOptions): Promise<TimeLapse>;
|
|
2589
|
+
/**
|
|
2590
|
+
* The painting repainted from its recorded strokes at `scale` times the size (brush sizes,
|
|
2591
|
+
* paper grain, fills and wet events scaled), wet paint dried: more detail instead of upscaled
|
|
2592
|
+
* pixels. Layers painted without recorded strokes (imported pixels) come out empty. Changes nothing here.
|
|
2593
|
+
*/
|
|
2594
|
+
/** `renderHighRes` as PNG bytes. */
|
|
2595
|
+
exportHighResPng(options: {
|
|
2596
|
+
scale: number;
|
|
2597
|
+
paper?: boolean;
|
|
2598
|
+
}): Promise<Uint8Array>;
|
|
2599
|
+
renderHighRes(options: {
|
|
2600
|
+
scale: number;
|
|
2601
|
+
paper?: boolean;
|
|
2602
|
+
}): Promise<PixelImage>;
|
|
2603
|
+
/**
|
|
2604
|
+
* Lets wet paint flow with the wall clock: call every frame with `performance.now()` (the
|
|
2605
|
+
* bindings do). Steps at the wet speed unless paused; resolves when the frame's steps are done.
|
|
2606
|
+
*/
|
|
2607
|
+
tickWet(now: number): Promise<void>;
|
|
2608
|
+
/** Resolves when the wet kernel is as fast as it will get here (`null` when nothing is wet yet). */
|
|
2609
|
+
wetReady(): Promise<WetBackendInfo | null>;
|
|
2610
|
+
/** Stops the wet simulation's worker threads (call when the editor goes away). */
|
|
2611
|
+
dispose(): void;
|
|
2612
|
+
/** Resolves when wet paint is between steps (edits, undo and exports can run). */
|
|
2613
|
+
wetIdle(): Promise<void>;
|
|
2614
|
+
private wetLayer;
|
|
2615
|
+
private checkRect;
|
|
2616
|
+
/** Wet media as panels and tools show it, with each wet layer's state. */
|
|
2617
|
+
wetState(): PaintWetStateInfo & {
|
|
2618
|
+
layers: WetLayerInfo[];
|
|
2619
|
+
backend: WetBackendInfo | null;
|
|
2620
|
+
message: string | null;
|
|
2621
|
+
};
|
|
2622
|
+
/** One layer's wet settings and state. */
|
|
2623
|
+
wetLayerInfo(layerId?: string): WetLayerInfo;
|
|
2624
|
+
/**
|
|
2625
|
+
* Lets the paint flow for `steps` simulation steps now (or `seconds` at the wet speed): what
|
|
2626
|
+
* agents call between strokes. Not an undo step (time passing never is). Returns the steps run
|
|
2627
|
+
* and which layers are still wet.
|
|
2628
|
+
*/
|
|
2629
|
+
advanceWet(options: {
|
|
2630
|
+
steps?: number;
|
|
2631
|
+
seconds?: number;
|
|
2632
|
+
}): Promise<{
|
|
2633
|
+
steps: number;
|
|
2634
|
+
clock: number;
|
|
2635
|
+
stillWet: boolean;
|
|
2636
|
+
layers: WetLayerInfo[];
|
|
2637
|
+
}>;
|
|
2638
|
+
/**
|
|
2639
|
+
* Dries wet paint at once: everything settles where it is and the water goes (one undo step).
|
|
2640
|
+
* On one layer or all, optionally only inside `rect`. Returns what dried and whether anything is
|
|
2641
|
+
* still wet.
|
|
2642
|
+
*/
|
|
2643
|
+
dryPaint(options?: {
|
|
2644
|
+
layerId?: string;
|
|
2645
|
+
rect?: RasterRect;
|
|
2646
|
+
}): {
|
|
2647
|
+
dried: Array<{
|
|
2648
|
+
layerId: string;
|
|
2649
|
+
bounds: RasterRect | null;
|
|
2650
|
+
}>;
|
|
2651
|
+
stillWet: boolean;
|
|
2652
|
+
};
|
|
2653
|
+
/**
|
|
2654
|
+
* Pre-wets the paper (wet-in-wet): fills the paper's water over `rect` (default the canvas) by
|
|
2655
|
+
* `amount` 0..1 on a layer's wet layer, so paint laid there spreads softly. One undo step.
|
|
2656
|
+
*/
|
|
2657
|
+
wetPaper(options?: {
|
|
2658
|
+
layerId?: string;
|
|
2659
|
+
rect?: RasterRect;
|
|
2660
|
+
amount?: number;
|
|
2661
|
+
}): {
|
|
2662
|
+
layerId: string;
|
|
2663
|
+
rect: RasterRect;
|
|
2664
|
+
wet: boolean;
|
|
2665
|
+
};
|
|
2666
|
+
/** Records a settings change as a wet event (not an undo step). */
|
|
2667
|
+
private recordWetSetting;
|
|
2668
|
+
/** Changes a layer's wet settings (viscosity, evaporation, granulation…). Editor settings saved with the document, not an undo step. */
|
|
2669
|
+
setWetParams(layerId: string | undefined, update: Partial<WetParams>): WetParams;
|
|
2670
|
+
/** Sets a layer's flow map (water pushed in a pattern). Saved with the document, not an undo step. */
|
|
2671
|
+
setFlowMap(layerId: string | undefined, update: Partial<WetFlowSpec>): WetFlowSpec;
|
|
2672
|
+
/** Tilts the canvas (water runs downhill; steep tilts make drips). Saved with the document, not an undo step. */
|
|
2673
|
+
setCanvasTilt(update: Partial<WetTilt>): WetTilt;
|
|
2674
|
+
/** Stops (or restarts) time: paused paint stays wet. Editor state. */
|
|
2675
|
+
setWetPaused(paused: boolean): boolean;
|
|
2676
|
+
/** Simulation steps per second while time runs (1..2000; default 120). Editor state. */
|
|
2677
|
+
setWetSpeed(speed: number): number;
|
|
2678
|
+
/** Wet strokes dry as soon as they end (each stroke still one undo step). Editor state. */
|
|
2679
|
+
setInstantDry(on: boolean): boolean;
|
|
2680
|
+
/** Shows or hides the water overlay (where the water is, view only). */
|
|
2681
|
+
setWaterOverlay(visible: boolean): boolean;
|
|
2682
|
+
/** The water overlay's tiles over a document rectangle (RGBA, view only). */
|
|
2683
|
+
waterOverlayTiles(rect: RasterRect): Array<{
|
|
2684
|
+
tx: number;
|
|
2685
|
+
ty: number;
|
|
2686
|
+
data: Uint8ClampedArray;
|
|
2687
|
+
}>;
|
|
1624
2688
|
private sources;
|
|
1625
2689
|
/** The layers with the live stroke's surface in place. */
|
|
1626
2690
|
private currentLayers;
|
|
@@ -1628,6 +2692,10 @@ declare class PaintEditorController {
|
|
|
1628
2692
|
protected underlayLayers(): ViewLayer[];
|
|
1629
2693
|
/** The tracing image resampled onto the canvas (cached until it or the canvas changes). */
|
|
1630
2694
|
private tracingNode;
|
|
2695
|
+
/** The layers with every wet layer dried where it is (what saving and exporting see). */
|
|
2696
|
+
private driedLayers;
|
|
2697
|
+
/** The layers with thick paint lit (each thick layer's pixels replaced by its lit tiles). */
|
|
2698
|
+
private litLayers;
|
|
1631
2699
|
private renderLayers;
|
|
1632
2700
|
/**
|
|
1633
2701
|
* Composites the viewport stack (paper, view-only underlays, layers with the live stroke) tile by
|
|
@@ -1644,6 +2712,15 @@ declare class PaintEditorController {
|
|
|
1644
2712
|
maxSide?: number;
|
|
1645
2713
|
}): PixelImage;
|
|
1646
2714
|
/** The document as JSON with inline PNG assets. */
|
|
2715
|
+
/** The document as saved: wet paint dried where it is (deterministically); wet settings as data. */
|
|
2716
|
+
private savedState;
|
|
2717
|
+
/** Wet settings saved with the document (`null` when they are all defaults). */
|
|
2718
|
+
private wetJSON;
|
|
2719
|
+
private readWet;
|
|
2720
|
+
/** Thick paint and light as saved (`layerChannels`, `lighting`), merged into the extensions. */
|
|
2721
|
+
private thickExtensions;
|
|
2722
|
+
/** Reads `layerChannels` and `lighting` from loaded extensions into the document. */
|
|
2723
|
+
private readThick;
|
|
1647
2724
|
toJSON(options?: {
|
|
1648
2725
|
strokes?: boolean;
|
|
1649
2726
|
}): SerializedPaintDocument;
|
|
@@ -1726,6 +2803,36 @@ declare class PaintEditorController {
|
|
|
1726
2803
|
/** The colour of a reference image at its pixel (x, y). */
|
|
1727
2804
|
pickReferenceColor(id: string, x: number, y: number): string;
|
|
1728
2805
|
private readGuides;
|
|
2806
|
+
/** The document's `stickers` without these layers (as a spread: `{}` when unchanged). */
|
|
2807
|
+
private stickersWithout;
|
|
2808
|
+
/** The next free sticker number (ids `st<n>` are unique across the document). */
|
|
2809
|
+
private nextStickerNumber;
|
|
2810
|
+
private stickerLayer;
|
|
2811
|
+
/** A layer's stickers, bottom first. */
|
|
2812
|
+
listStickers(layerId?: string): Sticker[];
|
|
2813
|
+
/** Replaces a layer's stickers and redraws its pixels from them (one undo step). */
|
|
2814
|
+
private commitStickers;
|
|
2815
|
+
/** Moves, turns, resizes, recolours or restamps one sticker (one undo step). Returns it. */
|
|
2816
|
+
updateSticker(layerId: string | undefined, id: string, update: Partial<Omit<Sticker, 'id'>>): Sticker;
|
|
2817
|
+
/** Brings stickers to the top (`front`) or bottom of their layer (one undo step). */
|
|
2818
|
+
arrangeStickers(layerId: string | undefined, ids: readonly string[], to: 'front' | 'back'): string[];
|
|
2819
|
+
/** Deletes stickers (`ids`, or all of them on the layer) (one undo step). Returns the ids removed. */
|
|
2820
|
+
removeStickers(layerId: string | undefined, ids?: readonly string[]): string[];
|
|
2821
|
+
/** Sets a layer's stickers (replays). */
|
|
2822
|
+
replaceStickers(layerId: string, stickers: readonly Sticker[]): void;
|
|
2823
|
+
private stencilCache;
|
|
2824
|
+
private nextStencilId;
|
|
2825
|
+
/** The mask stencils' coverage over the canvas (cached), or `null` when none masks. */
|
|
2826
|
+
private stencilSelection;
|
|
2827
|
+
/** The stencils. */
|
|
2828
|
+
listStencils(): Stencil[];
|
|
2829
|
+
/** Lays a stencil on the canvas (a ruler or arc to follow, a straight edge or shape to mask). Editor state saved with the document, not an undo step. */
|
|
2830
|
+
addStencil(input: StencilInput): Stencil;
|
|
2831
|
+
private requireStencil;
|
|
2832
|
+
/** Moves, reshapes, switches (guide / mask, invert) or hides a stencil. */
|
|
2833
|
+
updateStencil(id: string, update: Partial<Omit<Stencil, 'id' | 'kind'>>): Stencil;
|
|
2834
|
+
/** Takes stencils away (`ids`, or all of them). Returns the ids removed. */
|
|
2835
|
+
removeStencils(ids?: readonly string[]): string[];
|
|
1729
2836
|
/** Colour harmonies around a colour (default the paint colour) on the painter's RYB wheel or the RGB wheel. */
|
|
1730
2837
|
harmony(kind: HarmonyKind, options?: {
|
|
1731
2838
|
color?: string;
|
|
@@ -1990,7 +3097,7 @@ declare class PaintCanvasPresenter {
|
|
|
1990
3097
|
declare function guideOverlay(controller: PaintEditorController): Array<{
|
|
1991
3098
|
from: Point2;
|
|
1992
3099
|
to: Point2;
|
|
1993
|
-
kind: 'symmetry' | 'perspective' | 'snap';
|
|
3100
|
+
kind: 'symmetry' | 'perspective' | 'snap' | 'stencil';
|
|
1994
3101
|
}>;
|
|
1995
3102
|
/** One slider of a panel: a path into the brush (or paper) settings. */
|
|
1996
3103
|
interface SliderControl {
|
|
@@ -2045,6 +3152,14 @@ interface BrushEngineControls {
|
|
|
2045
3152
|
multicolor: boolean;
|
|
2046
3153
|
/** Load / Clean buttons and a swatch of what the brush carries. */
|
|
2047
3154
|
load: boolean;
|
|
3155
|
+
/** The wet tool menu (`wet.tool`, `WET_TOOLS`) and medium menu (`wet.medium`, `WET_MEDIA`). */
|
|
3156
|
+
wetTool?: boolean;
|
|
3157
|
+
/** The impasto tool menu (`impasto.tool`, `IMPASTO_TOOLS`). */
|
|
3158
|
+
impastoTool?: boolean;
|
|
3159
|
+
/** The particle kind and render menus (`particle.kind`, `particle.render`). */
|
|
3160
|
+
particleMenus?: boolean;
|
|
3161
|
+
/** The sticker stamp toggles (`sticker.stamps`, `STAMP_KINDS`). */
|
|
3162
|
+
stickerStamps?: boolean;
|
|
2048
3163
|
}
|
|
2049
3164
|
/**
|
|
2050
3165
|
* The controls the active brush's engine adds to the brush panel (`null` for the eraser):
|
|
@@ -2053,7 +3168,9 @@ interface BrushEngineControls {
|
|
|
2053
3168
|
* pigment choice; dab and mixer brushes also have the dirty and multi-colour controls. Pick-up
|
|
2054
3169
|
* and mix always go through `behavior`, which keeps a Mixer Brush's `mixer` block in sync.
|
|
2055
3170
|
*/
|
|
2056
|
-
declare function brushEngineControls(brush: Pick<PaintBrush, 'engine'>
|
|
3171
|
+
declare function brushEngineControls(brush: Pick<PaintBrush, 'engine'> & {
|
|
3172
|
+
wet?: PaintBrush['wet'];
|
|
3173
|
+
}): BrushEngineControls | null;
|
|
2057
3174
|
/** The multi-colour sliders for a mode: softness, and dabs per colour in `dab` mode. */
|
|
2058
3175
|
declare function multiColorSliders(mode: string): readonly SliderControl[];
|
|
2059
3176
|
/** A brush change setting `path` (e.g. `smudge.mixing`, `behavior.dirty`) to any value, keeping the rest of its block. */
|
|
@@ -2064,5 +3181,136 @@ declare function brushLoadColor(load: MixerLoad): string | null;
|
|
|
2064
3181
|
declare const PAD_SAMPLE_SIZES: readonly number[];
|
|
2065
3182
|
/** The averaging radius `mixingPadPick` takes for a sample size: 1 → 0, 3 → 1, 5 → 2 … */
|
|
2066
3183
|
declare function padSampleRadius(size: number): number;
|
|
3184
|
+
/** Labels of the wet tools, for the wet brush's tool menu. */
|
|
3185
|
+
declare const WET_TOOL_LABELS: Readonly<Record<WetTool, string>>;
|
|
3186
|
+
/** The wet panel's sliders for a layer's wet settings (paths into `WetParams`). */
|
|
3187
|
+
declare const WET_PARAM_SLIDERS: readonly SliderControl[];
|
|
3188
|
+
/** The canvas tilt sliders. */
|
|
3189
|
+
declare const WET_TILT_SLIDERS: readonly SliderControl[];
|
|
3190
|
+
/** The speed slider (steps per second). */
|
|
3191
|
+
declare const WET_SPEED_SLIDER: SliderControl;
|
|
3192
|
+
/**
|
|
3193
|
+
* Runs the wet paint's clock with the display: `controller.tickWet(now)` every animation frame
|
|
3194
|
+
* (it does nothing while nothing is wet or time is paused). Returns a function that stops it.
|
|
3195
|
+
* Bindings call it once per mounted canvas.
|
|
3196
|
+
*/
|
|
3197
|
+
declare function startWetClock(controller: PaintEditorController, frame?: (cb: (t: number) => void) => number, cancel?: (id: number) => void): () => void;
|
|
3198
|
+
/**
|
|
3199
|
+
* Draws the water overlay (blue where the surface is wet, faint where the paper is damp) into a
|
|
3200
|
+
* document-sized 2D context, clearing it first. Draws nothing when the overlay is off.
|
|
3201
|
+
*/
|
|
3202
|
+
declare function drawWaterOverlay(controller: PaintEditorController, ctx: Paint2DContext & {
|
|
3203
|
+
clearRect(x: number, y: number, w: number, h: number): void;
|
|
3204
|
+
}): number;
|
|
3205
|
+
/** Labels of the wet media, for the wet brush's medium menu. */
|
|
3206
|
+
declare const WET_MEDIUM_LABELS: Readonly<Record<WetMediumKind, string>>;
|
|
3207
|
+
/** Labels of the impasto tools. */
|
|
3208
|
+
declare const IMPASTO_TOOL_LABELS: Readonly<Record<ImpastoTool, string>>;
|
|
3209
|
+
/** The light sliders (thick paint and metallic paper). */
|
|
3210
|
+
declare const LIGHT_SLIDERS: readonly SliderControl[];
|
|
3211
|
+
/** Labels of the particle kinds and renders. */
|
|
3212
|
+
declare const PARTICLE_KIND_LABELS: Readonly<Record<ParticleKind, string>>;
|
|
3213
|
+
declare const PARTICLE_RENDER_LABELS: Readonly<Record<ParticleRender, string>>;
|
|
3214
|
+
/** Labels of the sticker stamps. */
|
|
3215
|
+
declare const STAMP_LABELS: Readonly<Record<StampKind, string>>;
|
|
3216
|
+
/** Toggles one stamp in a sticker brush's set (never leaving it empty). */
|
|
3217
|
+
declare function toggleStamp(stamps: readonly StampKind[], stamp: StampKind): StampKind[];
|
|
3218
|
+
/** The sticker panel's sliders for the selected sticker. */
|
|
3219
|
+
declare const STICKER_SLIDERS: readonly SliderControl[];
|
|
3220
|
+
/** Stencils a toolbar can lay with one click, centred on the canvas. */
|
|
3221
|
+
declare function quickStencil(kind: 'ruler' | 'circle' | 'ellipse-mask' | 'edge-mask', canvas: {
|
|
3222
|
+
width: number;
|
|
3223
|
+
height: number;
|
|
3224
|
+
}): StencilInput;
|
|
3225
|
+
|
|
3226
|
+
/**
|
|
3227
|
+
* Agent tools for wet media (watercolour and gouache on a simulated wet layer). Wet strokes are
|
|
3228
|
+
* painted with `paint_stroke` and a wet brush (`list_brushes` category `watercolor` or
|
|
3229
|
+
* `wet-tool`); these tools read and steer the wet paint. Every result says whether paint is still
|
|
3230
|
+
* wet. Settings (wet parameters, flow maps, tilt) are saved with the document but are not undo
|
|
3231
|
+
* steps; drying and wetting the paper are.
|
|
3232
|
+
*/
|
|
3233
|
+
|
|
3234
|
+
declare const WET_PARAM_SCHEMA: {
|
|
3235
|
+
readonly viscosity: {
|
|
3236
|
+
readonly type: "number";
|
|
3237
|
+
readonly minimum: 0;
|
|
3238
|
+
readonly maximum: 1;
|
|
3239
|
+
readonly description: string;
|
|
3240
|
+
};
|
|
3241
|
+
readonly evaporation: {
|
|
3242
|
+
readonly type: "number";
|
|
3243
|
+
readonly minimum: 0;
|
|
3244
|
+
readonly maximum: 1;
|
|
3245
|
+
readonly description: string;
|
|
3246
|
+
};
|
|
3247
|
+
readonly edgeDarkening: {
|
|
3248
|
+
readonly type: "number";
|
|
3249
|
+
readonly minimum: 0;
|
|
3250
|
+
readonly maximum: 1;
|
|
3251
|
+
readonly description: string;
|
|
3252
|
+
};
|
|
3253
|
+
readonly pigmentWeight: {
|
|
3254
|
+
readonly type: "number";
|
|
3255
|
+
readonly minimum: 0;
|
|
3256
|
+
readonly maximum: 1;
|
|
3257
|
+
readonly description: string;
|
|
3258
|
+
};
|
|
3259
|
+
readonly granulation: {
|
|
3260
|
+
readonly type: "number";
|
|
3261
|
+
readonly minimum: 0;
|
|
3262
|
+
readonly maximum: 1;
|
|
3263
|
+
readonly description: string;
|
|
3264
|
+
};
|
|
3265
|
+
readonly pickup: {
|
|
3266
|
+
readonly type: "number";
|
|
3267
|
+
readonly minimum: 0;
|
|
3268
|
+
readonly maximum: 1;
|
|
3269
|
+
readonly description: string;
|
|
3270
|
+
};
|
|
3271
|
+
readonly fixture: {
|
|
3272
|
+
readonly type: "number";
|
|
3273
|
+
readonly minimum: 0;
|
|
3274
|
+
readonly maximum: 1;
|
|
3275
|
+
readonly description: string;
|
|
3276
|
+
};
|
|
3277
|
+
readonly bloom: {
|
|
3278
|
+
readonly type: "number";
|
|
3279
|
+
readonly minimum: 0;
|
|
3280
|
+
readonly maximum: 1;
|
|
3281
|
+
readonly description: string;
|
|
3282
|
+
};
|
|
3283
|
+
readonly bleed: {
|
|
3284
|
+
readonly type: "number";
|
|
3285
|
+
readonly minimum: 0;
|
|
3286
|
+
readonly maximum: 1;
|
|
3287
|
+
readonly description: string;
|
|
3288
|
+
};
|
|
3289
|
+
};
|
|
3290
|
+
declare function createPaintWetTools(c: PaintEditorController): AgentTool[];
|
|
3291
|
+
|
|
3292
|
+
/**
|
|
3293
|
+
* Agent tools for thick paint and light: impasto strokes are painted with `paint_stroke` and an
|
|
3294
|
+
* impasto brush; these tools move the light, read and flatten a layer's relief, and switch instant
|
|
3295
|
+
* drying of wet strokes.
|
|
3296
|
+
*/
|
|
3297
|
+
|
|
3298
|
+
declare function createPaintThickTools(c: PaintEditorController): AgentTool[];
|
|
3299
|
+
|
|
3300
|
+
/**
|
|
3301
|
+
* Agent tools for replaying a painting from its data: a time-lapse (frames, a video project of the
|
|
3302
|
+
* frames and the ffmpeg command that renders it) and a high-resolution export repainted from the
|
|
3303
|
+
* recorded strokes.
|
|
3304
|
+
*/
|
|
3305
|
+
|
|
3306
|
+
declare function createPaintReplayTools(c: PaintEditorController): AgentTool[];
|
|
3307
|
+
|
|
3308
|
+
/**
|
|
3309
|
+
* Agent tools for stickers: stamps sprayed with a sticker brush (`paint_stroke`, list_brushes
|
|
3310
|
+
* category sticker) stay objects on their layer, which these tools list, move, turn, resize,
|
|
3311
|
+
* recolour, reorder and delete. Each change is one undo step and redraws the layer.
|
|
3312
|
+
*/
|
|
3313
|
+
|
|
3314
|
+
declare function createPaintStickerTools(c: PaintEditorController): AgentTool[];
|
|
2067
3315
|
|
|
2068
|
-
export { type Affine, BLEND_MENU_GROUPS, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, type BlendMenuGroup, BlendMode, BlendModeInfo, type BrushBehavior, type BrushBehaviorInput, type BrushDynamics, type BrushEngineControls, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, DEFAULT_BEHAVIOR, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_MULTI_COLOR, DEFAULT_PAPER, DEFAULT_SWATCHES, DEFAULT_VIEW, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, type ImageEncoder, type ImageFormat, type ImageNode, type LayerUpdate, type Linear2, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MULTI_COLOR_MODES, MixingPad, type MixingPadTool, type MultiColorMode, type MultiColorSettings, PAD_SAMPLE_SIZES, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, type PadMixOptions, type PadMixPart, type Paint2DContext, type PaintAgentCommand, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDabStroke, type PaintDabStrokeOptions, PaintDirector, type PaintDirectorOptions, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintPerformanceResult, type PaintPixelLayer, type PaintPointerEvent, PaintPresence, type PaintPresenceActor, type PaintPresenceState, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, SYMMETRY_MODES, type SavedMixingPadInfo, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, TileableNoise, type TracingImage, type ViewLayer, type ViewportBox, affineCopy, applyAffine, brushEngineControls, brushLoadColor, brushLoadForStroke, brushToJSON, clonePaper, colorHarmony, composeAffine, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, isWetCapable, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, multiColorForDab, multiColorGradient, multiColorPalette, multiColorSliders, newPaintLayerId, normalizeBehavior, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizeMultiColor, normalizePaintBrush, normalizePaper, normalizePerspective, normalizeRotation, normalizeSymmetry, normalizeView, padSampleRadius, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchBrushValue, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, rand01, randSigned, readPath, renderBrushThumbnail, renderColorWheel, renderPaper, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, strokeBounds, strokeDuration, strokeSchedule, sweepPath, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thumbnailStroke, tiltToOrientation, toStrokePoint, usesPaintExchange, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wheelAngle, wheelPick, wheelPoint, wheelToHue };
|
|
3316
|
+
export { type Affine, BLEND_MENU_GROUPS, BRUSH_ID, BRUSH_ROTATIONS, BRUSH_SLIDERS, BUILTIN_BRUSHES, type BlendMenuGroup, BlendMode, BlendModeInfo, type BristleSettings, BristleStroke, type BrushBehavior, type BrushBehaviorInput, type BrushDynamics, type BrushEngineControls, type BrushLibraryFile, type BrushRotation, type BrushThumbnail, type ColorWheel, type CoverageStroke, DEFAULT_BEHAVIOR, DEFAULT_BRISTLE, DEFAULT_BRUSH_ID, DEFAULT_DPI, DEFAULT_DYNAMICS, DEFAULT_IMPASTO, DEFAULT_LIGHTING, DEFAULT_MULTI_COLOR, DEFAULT_PAPER, DEFAULT_PARTICLE, DEFAULT_STICKER, DEFAULT_SWATCHES, DEFAULT_VIEW, DEFAULT_WET_BRUSH, DryStroke, type DryStrokeOptions, type FillLayerOptions, GRAIN_PATTERNS, GRAIN_TILE, type GrainPattern, type GrainTuning, HARMONY_KINDS, type Hand, type HarmonyColor, type HarmonyKind, IDENTITY_AFFINE, IDENTITY_COPY, IMPASTO_TOOLS, IMPASTO_TOOL_LABELS, type ImageEncoder, type ImageFormat, type ImageNode, type ImpastoSettings, type ImpastoTool, LIGHT_SLIDERS, type LayerUpdate, type Linear2, LitCache, type LoadedPaintDocument, MAX_CANVAS_SIDE, MAX_COORDINATE, MAX_IMAGE_SIDE, MAX_ZOOM, MIN_CANVAS_SIDE, MIN_ZOOM, MIXING_PAD_HEIGHT, MIXING_PAD_WIDTH, MULTI_COLOR_MODES, MixingPad, type MixingPadTool, type MultiColorMode, type MultiColorSettings, PAD_SAMPLE_SIZES, PAINT_BRUSH_CATEGORIES, PAINT_BRUSH_ENGINES, PAINT_FORMAT, PAINT_SCHEMA_VERSION, PAINT_TOOLS, PAPER_FAMILIES, PAPER_LIBRARY, PAPER_NODE_ID, PAPER_SLIDERS, PARTICLE_KINDS, PARTICLE_KIND_LABELS, PARTICLE_RENDERS, PARTICLE_RENDER_LABELS, type PadMixOptions, type PadMixPart, type Paint2DContext, type PaintAgentCommand, type PaintAssetStore, type PaintBrush, type PaintBrushCategory, type PaintBrushEngine, type PaintBrushInput, type PaintCanvas, PaintCanvasPresenter, PaintDabStroke, type PaintDabStrokeOptions, PaintDirector, type PaintDirectorOptions, PaintDocumentError, type PaintDocumentExtensions, type PaintDocumentState, PaintEditorController, PaintEditorError, type PaintEditorOptions, type PaintEditorState, type PaintEditorToolProviderOptions, type PaintEvent, PaintGesture, type PaintGroupLayer, type PaintGuides, type PaintLayer, type PaintLayerInfo, type PaintLighting, type PaintPerformanceResult, type PaintPixelLayer, type PaintPointerEvent, PaintPresence, type PaintPresenceActor, type PaintPresenceState, type PaintReplayOptions, type PaintStrokeInit, type PaintStrokeResult, PaintStrokeSession, type PaintTool, type PaintView, type PaintWetHost, PaintWetLayers, type PaintWetOptions, type PaintWetStateInfo, type PaperFamily, PaperGrain, type PaperNode, type PaperSettings, type PaperTiling, type ParticleKind, type ParticleRender, type ParticleSettings, ParticleStroke, type PenDab, PenDabWalker, type PenPoint, PenSmoother, type PerspectiveGuide, PerspectiveSnapper, type PixelImage, type Point2, type PointerSample, type ProceduralStrokeOptions, type RecordedStroke, type ReferenceImage, type RenderOptions, type ReplayFrame, type ReplayOptions, type Rgb255, SNAP_DECIDE_DISTANCE, STAMP_KINDS, STAMP_LABELS, STENCIL_KINDS, STENCIL_SHAPES, STICKER_SLIDERS, SYMMETRY_MODES, type SavedMixingPadInfo, type SavedThickChannels, type SerializeOptions, type SerializedPaintDocument, type SliderControl, type StampKind, type Stencil, type StencilInput, type StencilKind, type StencilShape, StencilSnapper, type Sticker, type StickerSettings, StickerSpray, type StickerSprayInit, type StrokeCopy, type StrokeSessionHost, type StrokeSessionInit, type StrokeVariation, type Swatch, type SymmetryGuide, type SymmetryMode, TRACING_NODE_ID, type ThickChannels, ThickStrokeDriver, type ThickStrokeInit, TileableNoise, type TimeLapse, type TimeLapseOptions, type TracingImage, type ViewLayer, type ViewportBox, WET_MEDIA, WET_MEDIUM_LABELS, WET_MEDIUM_PARAMS, WET_PARAM_SCHEMA, WET_PARAM_SLIDERS, WET_SPEED_SLIDER, WET_TILT_SLIDERS, WET_TOOLS, WET_TOOL_LABELS, WetBackendInfo, type WetBrushSettings, type WetEvent, WetFlowSpec, type WetLayerInfo, type WetLayerSettings, type WetMediumKind, WetParams, WetStrokeDriver, type WetStrokeInit, WetTilt, type WetTool, affineCopy, applyAffine, brushEngineControls, brushLoadColor, brushLoadForStroke, brushToJSON, buildTimeLapse, clonePaper, colorHarmony, composeAffine, contactSheet, createPaintCompositor, createPaintDocument, createPaintEditorToolProvider, createPaintLayer, createPaintReplayTools, createPaintStickerTools, createPaintThickTools, createPaintWetTools, decodeImageBlob, defaultGuides, defaultImageEncoder, deserializePaintDocument, deserializePaintDocumentAsync, documentExtensions, documentToScreen, downloadBytes, downsampleRgba, drawSticker, drawWaterOverlay, emptiedLayers, emptyThickChannels, fitZoom, grainCatch, grainHeights, guideOverlay, handlePaintShortcut, hash2, hexToRgb8, hslToRgb, hsvToRgb, hueToWheel, invertAffine, isPalmTouch, isWetBrush, isWetCapable, lightTile, lightingKey, mapPenPoint, mix32, mixerLoadForStroke, mixerTip, multiColorForDab, multiColorGradient, multiColorPalette, multiColorSliders, newPaintLayerId, normalizeBehavior, normalizeBristle, normalizeBrushStroke, normalizeCanvas, normalizeDynamics, normalizeImpasto, normalizeLighting, normalizeMultiColor, normalizePaintBrush, normalizePaper, normalizeParticle, normalizePerspective, normalizeRotation, normalizeStencil, normalizeStickerSettings, normalizeStickerUpdate, normalizeSymmetry, normalizeView, normalizeWetBrush, padSampleRadius, paperGrain, paperKey, paperNode, paperPreset, parseBrushLibrary, parseHexColor, patchBrushValue, patchPath, penOrientation, perspectiveDirections, perspectiveGuideLines, pointerToPenPoint, quickStencil, rand01, randSigned, readPath, reliefInfo, renderBrushThumbnail, renderColorWheel, renderPaper, renderStickers, replayFrame, replayItems, replayPainting, rgbToHexColor, rgbToHsl, rgbToHsv, samplePressure, scaleStencil, screenToDocument, seedFromString, serializePaintDocument, serializePaintDocumentExternal, stampCoverage, startWetClock, stencilMask, stencilOutline, stickerRect, stickerSurface, strokeBounds, strokeDuration, strokeSchedule, sweepPath, symmetryAxisAngles, symmetryAxisSegments, symmetryCopies, thickFromJSON, thickToJSON, thumbnailStroke, tiltToOrientation, timeLapseVideo, timeLapseZip, toStrokePoint, toggleStamp, usesPaintExchange, variationAffine, varyStroke, viewCssTransform, viewMatrix, viewPenToDocument, wetPaperInput, wetPigmentOf, wheelAngle, wheelPick, wheelPoint, wheelToHue };
|