@pixerate/editor 0.135.0 → 0.137.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animation-editor/index.cjs +63 -55
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +8 -5
- package/dist/animation-editor/index.d.ts +8 -5
- package/dist/animation-editor/index.js +10 -2
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/{bezier-J8i4uqSH.d.cts → bezier-CZ2AhjID.d.cts} +1 -1
- package/dist/{bezier-lNEOp0Jx.d.ts → bezier-Dywv3Bw8.d.ts} +1 -1
- package/dist/blending-D7jCmjO6.d.cts +1026 -0
- package/dist/blending-NgF3qo3d.d.ts +1026 -0
- package/dist/brushes-CnvZSYYU.d.cts +744 -0
- package/dist/brushes-DJmUbIgz.d.ts +744 -0
- package/dist/{brushstrokeRender-SII6MG4R.js → brushstrokeRender-2FAXA3XC.js} +13 -16
- package/dist/{brushstrokeRender-SII6MG4R.js.map → brushstrokeRender-2FAXA3XC.js.map} +1 -1
- package/dist/{brushstrokeRender-JNHVEUFG.cjs → brushstrokeRender-IMFY3FF6.cjs} +16 -19
- package/dist/brushstrokeRender-IMFY3FF6.cjs.map +1 -0
- package/dist/canvas/index.cjs +10 -10
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +10 -7
- package/dist/canvas/index.d.ts +10 -7
- package/dist/canvas/index.js +4 -4
- package/dist/{chunk-W4LNKTHT.cjs → chunk-2XS6LTUP.cjs} +63 -63
- package/dist/{chunk-W4LNKTHT.cjs.map → chunk-2XS6LTUP.cjs.map} +1 -1
- package/dist/{chunk-HHTCDFZC.js → chunk-3D77LOBM.js} +234 -136
- package/dist/chunk-3D77LOBM.js.map +1 -0
- package/dist/{chunk-BR7EKQUP.js → chunk-3G6J7UFV.js} +322 -2
- package/dist/chunk-3G6J7UFV.js.map +1 -0
- package/dist/{chunk-62XUO5LB.js → chunk-3X6IAJUQ.js} +2 -2
- package/dist/{chunk-EM3FMJWE.js → chunk-5FSRTP6E.js} +320 -3
- package/dist/chunk-5FSRTP6E.js.map +1 -0
- package/dist/{chunk-Z2H7KECK.cjs → chunk-5OOIUXNJ.cjs} +345 -247
- package/dist/chunk-5OOIUXNJ.cjs.map +1 -0
- package/dist/{chunk-7RJ6P7MI.js → chunk-6S6362D2.js} +2 -2
- package/dist/chunk-A3BVTI3G.js +150 -0
- package/dist/chunk-A3BVTI3G.js.map +1 -0
- package/dist/{chunk-24ATT2PE.cjs → chunk-AEAAN4AD.cjs} +120 -120
- package/dist/{chunk-24ATT2PE.cjs.map → chunk-AEAAN4AD.cjs.map} +1 -1
- package/dist/chunk-ECCXGELC.cjs +727 -0
- package/dist/chunk-ECCXGELC.cjs.map +1 -0
- package/dist/{chunk-SVYE5HIM.js → chunk-EQQMGO42.js} +2 -2
- package/dist/{chunk-FHPVDTI2.cjs → chunk-F2Y4E4GU.cjs} +22 -22
- package/dist/{chunk-FHPVDTI2.cjs.map → chunk-F2Y4E4GU.cjs.map} +1 -1
- package/dist/{chunk-OXSV64JX.js → chunk-G6IKY6PL.js} +4 -4
- package/dist/{chunk-UYQUPUR6.js → chunk-HUO3TI4C.js} +3 -3
- package/dist/chunk-I52DRGGJ.js +727 -0
- package/dist/chunk-I52DRGGJ.js.map +1 -0
- package/dist/{chunk-DDSO4HS7.cjs → chunk-KEENUAFN.cjs} +2 -2
- package/dist/{chunk-DDSO4HS7.cjs.map → chunk-KEENUAFN.cjs.map} +1 -1
- package/dist/{chunk-2HC4DRF4.js → chunk-LLFHCSU2.js} +2 -2
- package/dist/{chunk-J5BA3BQ6.cjs → chunk-O7LUD7MH.cjs} +323 -3
- package/dist/chunk-O7LUD7MH.cjs.map +1 -0
- package/dist/{chunk-ILYFOJUN.js → chunk-OIBIRXRG.js} +3 -3
- package/dist/{chunk-FWEO3HSL.js → chunk-PQQF5KOW.js} +8 -8
- package/dist/{chunk-T6TV6HY6.cjs → chunk-PRSRYHQD.cjs} +36 -36
- package/dist/{chunk-T6TV6HY6.cjs.map → chunk-PRSRYHQD.cjs.map} +1 -1
- package/dist/chunk-Q6OUXEK3.cjs +150 -0
- package/dist/chunk-Q6OUXEK3.cjs.map +1 -0
- package/dist/{chunk-6GFRKLLS.cjs → chunk-QBWEH3IM.cjs} +429 -112
- package/dist/chunk-QBWEH3IM.cjs.map +1 -0
- package/dist/{chunk-ESIWZMMK.js → chunk-SYT2TLQF.js} +35 -37
- package/dist/{chunk-ESIWZMMK.js.map → chunk-SYT2TLQF.js.map} +1 -1
- package/dist/{chunk-YT3JL7YM.cjs → chunk-TLCTTQFF.cjs} +53 -53
- package/dist/{chunk-YT3JL7YM.cjs.map → chunk-TLCTTQFF.cjs.map} +1 -1
- package/dist/{chunk-P3RQ5UJE.cjs → chunk-UTQKC66X.cjs} +4 -4
- package/dist/{chunk-P3RQ5UJE.cjs.map → chunk-UTQKC66X.cjs.map} +1 -1
- package/dist/{chunk-J3B45HTA.cjs → chunk-XB25WOC2.cjs} +6 -6
- package/dist/{chunk-J3B45HTA.cjs.map → chunk-XB25WOC2.cjs.map} +1 -1
- package/dist/{chunk-YAFS2QFZ.cjs → chunk-XN2NBLV5.cjs} +313 -315
- package/dist/chunk-XN2NBLV5.cjs.map +1 -0
- package/dist/effects/index.d.cts +7 -4
- package/dist/effects/index.d.ts +7 -4
- package/dist/{export-DMeG6E06.d.ts → export-41rL6Cne.d.ts} +1 -1
- package/dist/{export-AZSwX087.d.cts → export-CyJdfP_Z.d.cts} +5 -4
- package/dist/{export-KzubBKo7.d.cts → export-D9mXp05Q.d.cts} +1 -1
- package/dist/{export-BDkyDNM8.d.ts → export-DwnZ_hvQ.d.ts} +5 -4
- package/dist/extensions/index.cjs +3 -3
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +2 -2
- package/dist/generation/index.cjs +150 -945
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +3 -423
- package/dist/generation/index.d.ts +3 -423
- package/dist/generation/index.js +59 -854
- package/dist/generation/index.js.map +1 -1
- package/dist/grammar/index.cjs +3 -3
- package/dist/grammar/index.cjs.map +1 -1
- package/dist/grammar/index.js +2 -2
- package/dist/{helpers-Coa4e2j_.d.ts → helpers-C-RgeyvH.d.ts} +25 -8
- package/dist/{helpers-DHl28yLO.d.cts → helpers-v6GMazzg.d.cts} +25 -8
- package/dist/image-editor/index.cjs +986 -986
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +15 -11
- package/dist/image-editor/index.d.ts +15 -11
- package/dist/image-editor/index.js +94 -94
- package/dist/index.cjs +28 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +36 -36
- package/dist/{opentype-BWR7Qv8W.d.cts → opentype-KR_jj6b_.d.cts} +1 -1
- package/dist/{opentype-Dfcx6wFx.d.ts → opentype-a1KAW-qs.d.ts} +1 -1
- package/dist/paint/index.cjs +5 -8
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +4 -2
- package/dist/paint/index.d.ts +4 -2
- package/dist/paint/index.js +15 -18
- package/dist/paint-editor/index.cjs +405 -403
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +15 -748
- package/dist/paint-editor/index.d.ts +15 -748
- package/dist/paint-editor/index.js +79 -77
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/{pathfinder-D8KQ6blQ.d.cts → pathfinder-BHS4vQNm.d.cts} +1 -1
- package/dist/{pathfinder-Celbi3e6.d.ts → pathfinder-BVvTIBMo.d.ts} +1 -1
- package/dist/{pool-8JZGWHvR.d.ts → pool-CZQOrSXZ.d.ts} +1 -1
- package/dist/{pool-BfuxBKTd.d.cts → pool-FjZZmZ80.d.cts} +1 -1
- package/dist/provenance-DmlcgdzT.d.cts +426 -0
- package/dist/provenance-uh5r2TQ8.d.ts +426 -0
- package/dist/rand-8xbUb6ur.d.ts +1118 -0
- package/dist/rand-Cw9VLrVC.d.cts +1118 -0
- package/dist/raster/index.cjs +17 -19
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.js +9 -11
- package/dist/raster/index.js.map +1 -1
- package/dist/{svg-CxxqG3_W.d.ts → svg-CSz3mzkw.d.ts} +2 -2
- package/dist/{svg-pwXWq6s2.d.cts → svg-DTb40ICU.d.cts} +2 -2
- package/dist/{tools-y1M6O3UT.d.ts → tools-B0wXP5cF.d.ts} +1 -1
- package/dist/{tools-3EBjR91b.d.cts → tools-Ca0kI1qo.d.cts} +1 -1
- package/dist/vector/index.cjs +2698 -1674
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +390 -72
- package/dist/vector/index.d.ts +390 -72
- package/dist/vector/index.js +1665 -641
- package/dist/vector/index.js.map +1 -1
- package/dist/video-editor/index.d.cts +7 -4
- package/dist/video-editor/index.d.ts +7 -4
- package/dist/{warp-Ceqdz7kj.d.cts → warp-DTOp0lON.d.cts} +1 -1
- package/dist/{warp-C1gwu2G_.d.ts → warp-pd7hrH4T.d.ts} +1 -1
- package/package.json +1 -1
- package/skills/pixerate-vector/SKILL.md +22 -0
- package/dist/brushstrokeRender-JNHVEUFG.cjs.map +0 -1
- package/dist/chunk-64PYNEHJ.js +0 -340
- package/dist/chunk-64PYNEHJ.js.map +0 -1
- package/dist/chunk-6GFRKLLS.cjs.map +0 -1
- package/dist/chunk-7QN76ESJ.js +0 -323
- package/dist/chunk-7QN76ESJ.js.map +0 -1
- package/dist/chunk-BR7EKQUP.js.map +0 -1
- package/dist/chunk-DCLJNVZS.cjs +0 -323
- package/dist/chunk-DCLJNVZS.cjs.map +0 -1
- package/dist/chunk-EM3FMJWE.js.map +0 -1
- package/dist/chunk-HHTCDFZC.js.map +0 -1
- package/dist/chunk-J5BA3BQ6.cjs.map +0 -1
- package/dist/chunk-YAFS2QFZ.cjs.map +0 -1
- package/dist/chunk-Z2H7KECK.cjs.map +0 -1
- package/dist/chunk-Z6OUCS2K.cjs +0 -340
- package/dist/chunk-Z6OUCS2K.cjs.map +0 -1
- package/dist/rand-BszBST4s.d.ts +0 -2140
- package/dist/rand-DOH_aMvm.d.cts +0 -2140
- /package/dist/{chunk-62XUO5LB.js.map → chunk-3X6IAJUQ.js.map} +0 -0
- /package/dist/{chunk-7RJ6P7MI.js.map → chunk-6S6362D2.js.map} +0 -0
- /package/dist/{chunk-SVYE5HIM.js.map → chunk-EQQMGO42.js.map} +0 -0
- /package/dist/{chunk-OXSV64JX.js.map → chunk-G6IKY6PL.js.map} +0 -0
- /package/dist/{chunk-UYQUPUR6.js.map → chunk-HUO3TI4C.js.map} +0 -0
- /package/dist/{chunk-2HC4DRF4.js.map → chunk-LLFHCSU2.js.map} +0 -0
- /package/dist/{chunk-ILYFOJUN.js.map → chunk-OIBIRXRG.js.map} +0 -0
- /package/dist/{chunk-FWEO3HSL.js.map → chunk-PQQF5KOW.js.map} +0 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import { D as Dab, C as CoverageMap, B as BrushSettings, S as StrokePoint, P as PaintTileTarget, a as PaintGradientSource, T as TileMedium, M as MixerSettings, b as MixingModel, c as BlenderSettings, d as SmudgeSettings, e as PaintSource } from './blending-NgF3qo3d.js';
|
|
2
|
+
import { R as RasterRect, T as TiledSurface } from './surface-1MRncXpF.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Paper: a generated grain height map (0 = valley, 1 = peak) that tiles across the canvas, a paper
|
|
6
|
+
* colour, grain depth, and the three properties wet media will read later (absorption,
|
|
7
|
+
* permeability, roughness). Dry media catch the peaks: a light stroke only marks the high grain, a
|
|
8
|
+
* firm one fills the valleys too. Everything is generated from a pattern and a seed, so a paper is
|
|
9
|
+
* a few numbers in the document, never a bitmap, and no third-party texture is bundled.
|
|
10
|
+
*/
|
|
11
|
+
/** Grain generators. Each is our own procedural pattern (no scanned textures). */
|
|
12
|
+
type GrainPattern = 'smooth' | 'cold-pressed' | 'hot-pressed' | 'rough' | 'vellum' | 'laid' | 'ingres' | 'sanded' | 'canvas' | 'linen' | 'gesso';
|
|
13
|
+
declare const GRAIN_PATTERNS: readonly GrainPattern[];
|
|
14
|
+
/** Paper families, as a library groups them. */
|
|
15
|
+
type PaperFamily = 'watercolor' | 'drawing' | 'pastel' | 'canvas' | 'panel' | 'smooth';
|
|
16
|
+
declare const PAPER_FAMILIES: readonly PaperFamily[];
|
|
17
|
+
/** How a height map is tuned for one kind of media. */
|
|
18
|
+
interface GrainTuning {
|
|
19
|
+
/** Height contrast around the middle, 0..3 (default 1). */
|
|
20
|
+
contrast: number;
|
|
21
|
+
/** Swap peaks and valleys (default false). */
|
|
22
|
+
invert: boolean;
|
|
23
|
+
}
|
|
24
|
+
type PaperTiling = 'repeat' | 'mirror';
|
|
25
|
+
interface PaperSettings {
|
|
26
|
+
/** The library preset this paper started from (`custom` when it matches none). */
|
|
27
|
+
preset: string;
|
|
28
|
+
name: string;
|
|
29
|
+
family: PaperFamily;
|
|
30
|
+
pattern: GrainPattern;
|
|
31
|
+
/** Grain seed: another seed gives another sheet of the same paper. */
|
|
32
|
+
seed: number;
|
|
33
|
+
/** Paper colour #rrggbb. */
|
|
34
|
+
color: string;
|
|
35
|
+
/** Grain scale: 1 is the pattern's natural size, 2 twice as coarse (0.25..8). */
|
|
36
|
+
scale: number;
|
|
37
|
+
/** How the grain tile repeats: `repeat` or `mirror` (alternate tiles flipped, so seams vanish). */
|
|
38
|
+
tiling: PaperTiling;
|
|
39
|
+
/** Grain depth 0..1: how much grain-responsive dry media skip the valleys (0: none). */
|
|
40
|
+
depth: number;
|
|
41
|
+
/** How much the grain shades the paper colour on screen and in exports, 0..1. */
|
|
42
|
+
shading: number;
|
|
43
|
+
/** The height map as dry media read it. */
|
|
44
|
+
dry: GrainTuning;
|
|
45
|
+
/** The height map as wet media will read it (stored now; wet media is a later phase). */
|
|
46
|
+
wet: GrainTuning;
|
|
47
|
+
/** Wet media: how fast the paper takes up water, 0..1. */
|
|
48
|
+
absorption: number;
|
|
49
|
+
/** Wet media: how far water spreads inside the paper, 0..1. */
|
|
50
|
+
permeability: number;
|
|
51
|
+
/** Wet media: how much the grain catches pigment, 0..1. */
|
|
52
|
+
roughness: number;
|
|
53
|
+
/** Metallic paper (gold or silver leaf, pearlescent card): the grain catches the light, 0..1 (default 0). */
|
|
54
|
+
metallic?: number;
|
|
55
|
+
}
|
|
56
|
+
/** Side of the generated grain tile in pixels (at scale 1). */
|
|
57
|
+
declare const GRAIN_TILE = 256;
|
|
58
|
+
type PaperPreset = PaperSettings;
|
|
59
|
+
/** The built-in paper library: generated papers in families. */
|
|
60
|
+
declare const PAPER_LIBRARY: readonly PaperPreset[];
|
|
61
|
+
declare const DEFAULT_PAPER: Readonly<PaperSettings>;
|
|
62
|
+
/** A paper from the library by id, or `undefined`. */
|
|
63
|
+
declare function paperPreset(id: string): PaperSettings | undefined;
|
|
64
|
+
declare function clonePaper(p: PaperSettings): PaperSettings;
|
|
65
|
+
/**
|
|
66
|
+
* A paper with every field in range. Missing fields come from `base` (default: the library preset
|
|
67
|
+
* named by `raw.preset`, else cold pressed).
|
|
68
|
+
*/
|
|
69
|
+
declare function normalizePaper(raw?: Partial<PaperSettings> | Record<string, unknown>, base?: PaperSettings): PaperSettings;
|
|
70
|
+
/** The raw height map of a pattern (`GRAIN_TILE` square, 0..1), generated once per pattern and seed. */
|
|
71
|
+
declare function grainHeights(pattern: GrainPattern, seed: number, size?: number): Float32Array;
|
|
72
|
+
/**
|
|
73
|
+
* A paper's height field over the canvas (document pixels), tuned for one kind of media. Read it
|
|
74
|
+
* with `at(x, y)`: bilinear, tiled by `paper.tiling`, at `paper.scale`.
|
|
75
|
+
*/
|
|
76
|
+
declare class PaperGrain {
|
|
77
|
+
readonly size = 256;
|
|
78
|
+
private readonly heights;
|
|
79
|
+
private readonly scale;
|
|
80
|
+
private readonly mirror;
|
|
81
|
+
readonly flat: boolean;
|
|
82
|
+
constructor(paper: PaperSettings, media?: 'dry' | 'wet');
|
|
83
|
+
/** Tile cell index for a coordinate in grain pixels (mirrored on odd tiles when mirroring). */
|
|
84
|
+
private wrap;
|
|
85
|
+
/** Height 0..1 at document position (x, y). */
|
|
86
|
+
at(x: number, y: number): number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* How much of a dry dab lands at grain height `h` (0..1): the media catches the peaks first, and
|
|
90
|
+
* pressure pushes it into the valleys. `depth` is the paper's grain depth times the brush's grain
|
|
91
|
+
* response; at 0 everything lands (1).
|
|
92
|
+
*/
|
|
93
|
+
declare function grainCatch(h: number, depth: number, pressure: number): number;
|
|
94
|
+
/** `#rrggbb` → [r, g, b] 0..255. */
|
|
95
|
+
declare function hexToRgb8(hex: string): [number, number, number];
|
|
96
|
+
/**
|
|
97
|
+
* The paper as opaque RGBA over `rect` (document pixels): its colour, darkened in the valleys by
|
|
98
|
+
* `shading`.
|
|
99
|
+
*/
|
|
100
|
+
declare function renderPaper(paper: PaperSettings, rect: {
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
}, grain?: PaperGrain, light?: {
|
|
106
|
+
azimuth: number;
|
|
107
|
+
elevation: number;
|
|
108
|
+
}): Uint8ClampedArray;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The paint editor's dry-media stroke engine, on the shared brush engine (`@pixerate/editor/paint`):
|
|
112
|
+
*
|
|
113
|
+
* - Pen input: pressure, tilt (`tiltX` / `tiltY` or `altitude` / `azimuth`), barrel rotation
|
|
114
|
+
* (`twist`) and time (for velocity). Position and pressure are smoothed separately.
|
|
115
|
+
* - `PenDabWalker` walks the points into the shared `Dab`s at `spacing × diameter`, with every
|
|
116
|
+
* pen channel interpolated, and applies the brush's dynamics, rotation mode and jitter.
|
|
117
|
+
* Jitter is a pure hash of the stroke seed and the dab index, so a stroke replays exactly.
|
|
118
|
+
* - `DryStroke` rasterizes each dab with the shared round tip, multiplies it by how much of it the
|
|
119
|
+
* paper grain catches (`grainCatch`), and builds it up in the shared `CoverageMap`; the session
|
|
120
|
+
* then composites the coverage with the shared `applyStrokeToTile`.
|
|
121
|
+
* - Symmetry: one stroke can paint several copies of its points (mirror and kaleidoscope), each
|
|
122
|
+
* with its own walker, into the same coverage, so the copies are one stroke and one undo step.
|
|
123
|
+
*
|
|
124
|
+
* Pure data in, pixels out: no DOM, no framework.
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/** One pen sample in document pixels. Every channel but x and y is optional. */
|
|
128
|
+
interface PenPoint {
|
|
129
|
+
x: number;
|
|
130
|
+
y: number;
|
|
131
|
+
/** 0..1 (missing: 1, a mouse). */
|
|
132
|
+
pressure?: number;
|
|
133
|
+
/** Milliseconds since any fixed origin (for velocity; missing: no velocity dynamics). */
|
|
134
|
+
t?: number;
|
|
135
|
+
/** Pointer Events tilt in degrees, -90..90 (used when `altitude` / `azimuth` are missing). */
|
|
136
|
+
tiltX?: number;
|
|
137
|
+
tiltY?: number;
|
|
138
|
+
/** Radians from the surface: π/2 is upright, 0 lies flat. */
|
|
139
|
+
altitude?: number;
|
|
140
|
+
/** Radians clockwise from the +x axis (screen y points down): where the pen leans. */
|
|
141
|
+
azimuth?: number;
|
|
142
|
+
/** Barrel rotation in degrees clockwise, 0..359. */
|
|
143
|
+
twist?: number;
|
|
144
|
+
}
|
|
145
|
+
/** A pen's lean as altitude and azimuth, or `null` when the sample has no tilt. */
|
|
146
|
+
declare function penOrientation(p: PenPoint): {
|
|
147
|
+
altitude: number;
|
|
148
|
+
azimuth: number;
|
|
149
|
+
} | null;
|
|
150
|
+
/** Pointer Events `tiltX` / `tiltY` (degrees) → altitude and azimuth (radians). */
|
|
151
|
+
declare function tiltToOrientation(tiltX: number, tiltY: number): {
|
|
152
|
+
altitude: number;
|
|
153
|
+
azimuth: number;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Exponential smoothing of position and of pressure, each by its own amount (0..0.95). Feed raw
|
|
157
|
+
* samples; the first sample passes through.
|
|
158
|
+
*/
|
|
159
|
+
declare class PenSmoother {
|
|
160
|
+
private pos;
|
|
161
|
+
private pressure;
|
|
162
|
+
private readonly a;
|
|
163
|
+
private readonly ap;
|
|
164
|
+
constructor(position: number, pressure: number);
|
|
165
|
+
push(p: PenPoint): PenPoint;
|
|
166
|
+
/** Where the smoothed position is now (or `null` before the first sample). */
|
|
167
|
+
get position(): {
|
|
168
|
+
x: number;
|
|
169
|
+
y: number;
|
|
170
|
+
} | null;
|
|
171
|
+
}
|
|
172
|
+
/** A linear map of document vectors `[a, b, c, d]`: (x, y) → (a·x + b·y, c·x + d·y). */
|
|
173
|
+
type Linear2 = readonly [number, number, number, number];
|
|
174
|
+
/** How one copy of a stroke maps the input: a point map and its linear part (for dab angles). */
|
|
175
|
+
interface StrokeCopy {
|
|
176
|
+
map(p: PenPoint): PenPoint;
|
|
177
|
+
linear: Linear2;
|
|
178
|
+
}
|
|
179
|
+
declare const IDENTITY_COPY: StrokeCopy;
|
|
180
|
+
/** A dab with the pen state it was made at (the shared `Dab` plus pressure for the grain). */
|
|
181
|
+
interface PenDab extends Dab {
|
|
182
|
+
pressure: number;
|
|
183
|
+
/** Direction of travel at the dab, radians counter-clockwise with y up (as `angle`). */
|
|
184
|
+
direction: number;
|
|
185
|
+
/** The dab's index within its copy of the stroke (0, 1, 2 …). */
|
|
186
|
+
index: number;
|
|
187
|
+
/** Path length (px, along the smoothed samples of this copy) travelled to the dab: what gradient paint maps by distance. */
|
|
188
|
+
travel: number;
|
|
189
|
+
/** ms since the stroke's first timed sample, or `null` when the samples carry no time. */
|
|
190
|
+
time: number | null;
|
|
191
|
+
/** Pen lean, 0 upright .. 1 flat. */
|
|
192
|
+
lean: number;
|
|
193
|
+
/** Pen speed (px/ms), or `null` when the samples carry no time. */
|
|
194
|
+
speed: number | null;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Walks pen samples into dabs, incrementally (any chunking gives the same dabs). Dab angles are in
|
|
198
|
+
* the shared rasterizer's convention: radians, counter-clockwise with y up.
|
|
199
|
+
*/
|
|
200
|
+
declare class PenDabWalker {
|
|
201
|
+
readonly brush: PaintBrush;
|
|
202
|
+
private readonly seed;
|
|
203
|
+
private readonly linear;
|
|
204
|
+
private last;
|
|
205
|
+
private nextAt;
|
|
206
|
+
private pendingFirst;
|
|
207
|
+
private index;
|
|
208
|
+
private dir;
|
|
209
|
+
private lastT;
|
|
210
|
+
private firstT;
|
|
211
|
+
private speed;
|
|
212
|
+
private travelled;
|
|
213
|
+
constructor(brush: PaintBrush, seed?: number, linear?: Linear2);
|
|
214
|
+
private sample;
|
|
215
|
+
private diameter;
|
|
216
|
+
private stepLen;
|
|
217
|
+
/** A rasterizer angle (CCW, y up) mapped through this copy's linear part. */
|
|
218
|
+
private mapAngle;
|
|
219
|
+
private dab;
|
|
220
|
+
push(p: PenPoint, out: PenDab[]): void;
|
|
221
|
+
finish(out: PenDab[]): void;
|
|
222
|
+
}
|
|
223
|
+
interface DryStrokeOptions {
|
|
224
|
+
/** The paper's dry height field, or `null` for no grain. */
|
|
225
|
+
grain: PaperGrain | null;
|
|
226
|
+
/** The paper's grain depth, 0..1 (multiplied by the brush's grain response). */
|
|
227
|
+
paperDepth: number;
|
|
228
|
+
/** Jitter seed (record it to replay the stroke exactly). */
|
|
229
|
+
seed?: number;
|
|
230
|
+
/** Copies of the stroke (symmetry); default one identity copy. */
|
|
231
|
+
copies?: readonly StrokeCopy[];
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* One dab-engine stroke (dab and eraser brushes): smoothing, the dab walk of every copy, grain and
|
|
235
|
+
* build-up in a shared coverage map.
|
|
236
|
+
*/
|
|
237
|
+
declare class DryStroke {
|
|
238
|
+
readonly brush: PaintBrush;
|
|
239
|
+
private readonly options;
|
|
240
|
+
readonly coverage: CoverageMap;
|
|
241
|
+
dabCount: number;
|
|
242
|
+
private readonly smoother;
|
|
243
|
+
private readonly walkers;
|
|
244
|
+
private readonly copies;
|
|
245
|
+
private readonly depth;
|
|
246
|
+
private buf;
|
|
247
|
+
private lastRaw;
|
|
248
|
+
private lastFed;
|
|
249
|
+
constructor(brush: PaintBrush, options: DryStrokeOptions);
|
|
250
|
+
/** Feeds raw pen samples; returns the document rectangles the new dabs touched. */
|
|
251
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
252
|
+
/** Ends the stroke: the smoothed path catches up with the last raw sample; a single tap paints one dab. */
|
|
253
|
+
finish(): RasterRect[];
|
|
254
|
+
private paint;
|
|
255
|
+
}
|
|
256
|
+
/** The shared engine's tip for the Mixer Brush engine (it smooths and walks by itself, so smoothing is applied before). */
|
|
257
|
+
declare function mixerTip(brush: PaintBrush): BrushSettings;
|
|
258
|
+
/** Pen samples → the shared engine's stroke points (pressure only). */
|
|
259
|
+
declare function toStrokePoint(p: PenPoint): StrokePoint;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Stickers: textured stamps sprayed along a stroke onto a sticker layer, where each stays an object
|
|
263
|
+
* that can be moved, turned, resized, recoloured or deleted afterwards. A sticker layer is a pixel
|
|
264
|
+
* layer whose pixels are drawn from its stickers (`renderStickers`), so the compositor, exports and
|
|
265
|
+
* every layer operation see ordinary pixels.
|
|
266
|
+
*
|
|
267
|
+
* The stamps are our own procedural shapes (dot, star, heart, leaf, petal flower, confetti, splat,
|
|
268
|
+
* sparkle, ring), each with a little shading and a seeded paper-like texture, so no artwork is
|
|
269
|
+
* bundled. The spray (`StickerSpray`) is deterministic: the same points, seed and brush give the
|
|
270
|
+
* same stickers.
|
|
271
|
+
*/
|
|
272
|
+
|
|
273
|
+
type StampKind = 'dot' | 'star' | 'heart' | 'leaf' | 'flower' | 'confetti' | 'splat' | 'sparkle' | 'ring';
|
|
274
|
+
declare const STAMP_KINDS: readonly StampKind[];
|
|
275
|
+
/** One sticker (document pixels). */
|
|
276
|
+
interface Sticker {
|
|
277
|
+
id: string;
|
|
278
|
+
stamp: StampKind;
|
|
279
|
+
x: number;
|
|
280
|
+
y: number;
|
|
281
|
+
/** Diameter in pixels. */
|
|
282
|
+
size: number;
|
|
283
|
+
/** Degrees clockwise. */
|
|
284
|
+
rotation: number;
|
|
285
|
+
/** #rrggbb */
|
|
286
|
+
color: string;
|
|
287
|
+
/** 0..1 */
|
|
288
|
+
opacity: number;
|
|
289
|
+
/** Texture seed. */
|
|
290
|
+
seed: number;
|
|
291
|
+
}
|
|
292
|
+
/** Sticker spray settings (each 0..1 unless noted). */
|
|
293
|
+
interface StickerSettings {
|
|
294
|
+
/** The stamps to spray (picked at random per sticker). */
|
|
295
|
+
stamps: StampKind[];
|
|
296
|
+
/** Stickers per tip diameter of travel. */
|
|
297
|
+
density: number;
|
|
298
|
+
/** Random size change. */
|
|
299
|
+
sizeJitter: number;
|
|
300
|
+
/** Random turn, as a fraction of ±180°. */
|
|
301
|
+
rotationJitter: number;
|
|
302
|
+
/** How far from the path stickers land, in tip diameters (0..2). */
|
|
303
|
+
scatter: number;
|
|
304
|
+
/** Random colour change (hue and lightness). */
|
|
305
|
+
colorJitter: number;
|
|
306
|
+
}
|
|
307
|
+
declare const DEFAULT_STICKER: Readonly<StickerSettings>;
|
|
308
|
+
declare function normalizeStickerSettings(raw?: Partial<StickerSettings>, base?: StickerSettings): StickerSettings;
|
|
309
|
+
/**
|
|
310
|
+
* Coverage of a stamp at a point (u, v) of its unit square (−1..1, y down), 0..1, before shading.
|
|
311
|
+
* `t` is the sticker's seed for shapes with random parts (splat, confetti).
|
|
312
|
+
*/
|
|
313
|
+
declare function stampCoverage(kind: StampKind, u: number, v: number, t: number): number;
|
|
314
|
+
/** The pixel rectangle a sticker covers. */
|
|
315
|
+
declare function stickerRect(s: Sticker): RasterRect;
|
|
316
|
+
/**
|
|
317
|
+
* Draws one sticker through a `TileMedium` (straight source-over): the stamp's coverage, lit a
|
|
318
|
+
* little from the top left and given a fine seeded texture, in its colour and opacity.
|
|
319
|
+
*/
|
|
320
|
+
declare function drawSticker(medium: TileMedium, s: Sticker): RasterRect;
|
|
321
|
+
interface StickerSprayInit {
|
|
322
|
+
brush: PaintBrush;
|
|
323
|
+
target: PaintTileTarget;
|
|
324
|
+
/** The paint colour #rrggbb. */
|
|
325
|
+
color: string;
|
|
326
|
+
seed: number;
|
|
327
|
+
copies: readonly StrokeCopy[];
|
|
328
|
+
/** The next sticker number on the layer (ids `st<n>`). */
|
|
329
|
+
nextId: number;
|
|
330
|
+
/** Gradient paint (resolved): each sticker takes its dab's colour (across the tip: by its offset). */
|
|
331
|
+
paint?: PaintGradientSource | null;
|
|
332
|
+
}
|
|
333
|
+
/** A sticker-spray stroke: drops stickers along the path and draws them as it goes. */
|
|
334
|
+
declare class StickerSpray {
|
|
335
|
+
readonly init: StickerSprayInit;
|
|
336
|
+
dabCount: number;
|
|
337
|
+
readonly stickers: Sticker[];
|
|
338
|
+
private readonly smoother;
|
|
339
|
+
private readonly walkers;
|
|
340
|
+
private readonly copies;
|
|
341
|
+
private readonly medium;
|
|
342
|
+
private lastRaw;
|
|
343
|
+
private lastFed;
|
|
344
|
+
private next;
|
|
345
|
+
constructor(init: StickerSprayInit);
|
|
346
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
347
|
+
finish(): RasterRect[];
|
|
348
|
+
private drop;
|
|
349
|
+
private sampler;
|
|
350
|
+
/** A sticker's colour: the paint colour, or gradient paint at its dab (`dx`, `dy` from the dab's centre). */
|
|
351
|
+
private colorAt;
|
|
352
|
+
}
|
|
353
|
+
/** A sticker list rendered over a transparent layer, through a tile target. */
|
|
354
|
+
declare function renderStickers(stickers: readonly Sticker[], target: PaintTileTarget): void;
|
|
355
|
+
/** Checks and rounds a sticker change; throws a sentence naming the bad field. */
|
|
356
|
+
declare function normalizeStickerUpdate(u: Partial<Omit<Sticker, 'id'>>): Partial<Omit<Sticker, 'id'>>;
|
|
357
|
+
/** A sticker layer's pixels: its stickers drawn bottom first over a transparent layer. */
|
|
358
|
+
declare function stickerSurface(stickers: readonly Sticker[]): TiledSurface;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Procedural brush engines: marks made by small simulations driven by the pen, rather than by
|
|
362
|
+
* stamping the tip.
|
|
363
|
+
*
|
|
364
|
+
* - **Particle brushes** (`engine: 'particle'`): a handful of particles follow the pen by their own
|
|
365
|
+
* rules. `string`: strands of masses on springs, dragged behind the pen like hair or wet string;
|
|
366
|
+
* `spring`: each particle tied to the pen by a spring, overshooting and wobbling; `orbit`:
|
|
367
|
+
* particles circling the pen (spirograph curls); `flow`: particles thrown off the pen and carried
|
|
368
|
+
* by a swirling flow field until they fade. Each is drawn as `dot`s, a continuous `line`, or a
|
|
369
|
+
* `sequence` of beads spaced along its path.
|
|
370
|
+
* - **Bristle brushes** (`engine: 'bristle'`): a tuft of bristles, each its own thin line. Pressure
|
|
371
|
+
* splays the tuft, tilt flattens it, and bristles lag behind the tip by their stiffness, so they
|
|
372
|
+
* bend into curves at turns; each bristle carries its own paint and runs dry at its own pace, so
|
|
373
|
+
* long strokes break into dry-brush streaks.
|
|
374
|
+
*
|
|
375
|
+
* Both are deterministic (seeded) and step the simulation by distance travelled, not by how the
|
|
376
|
+
* input was sampled, so a replay paints the same marks. They build up a `CoverageMap` like the dab
|
|
377
|
+
* engine, so opacity, blend modes, locks, stencils and symmetry work as for any dry brush.
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
interface ProceduralStrokeOptions {
|
|
381
|
+
grain: PaperGrain | null;
|
|
382
|
+
paperDepth: number;
|
|
383
|
+
seed?: number;
|
|
384
|
+
copies?: readonly StrokeCopy[];
|
|
385
|
+
/** Gradient paint (resolved): each mark takes the colour of the pen dab that made it. */
|
|
386
|
+
paint?: PaintGradientSource | null;
|
|
387
|
+
}
|
|
388
|
+
/** What a coverage-building stroke engine provides to the stroke session. */
|
|
389
|
+
interface CoverageStroke {
|
|
390
|
+
readonly coverage: CoverageMap;
|
|
391
|
+
dabCount: number;
|
|
392
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
393
|
+
finish(): RasterRect[];
|
|
394
|
+
}
|
|
395
|
+
/** Shared plumbing: smoothing, the dab walk per copy (for size, pressure, direction), grain, and stamping marks. */
|
|
396
|
+
declare abstract class MarkStroke implements CoverageStroke {
|
|
397
|
+
readonly brush: PaintBrush;
|
|
398
|
+
protected readonly options: ProceduralStrokeOptions;
|
|
399
|
+
readonly coverage: CoverageMap;
|
|
400
|
+
dabCount: number;
|
|
401
|
+
protected readonly smoother: PenSmoother;
|
|
402
|
+
protected readonly copies: readonly StrokeCopy[];
|
|
403
|
+
protected readonly walkers: PenDabWalker[];
|
|
404
|
+
protected readonly depth: number;
|
|
405
|
+
private buf;
|
|
406
|
+
private lastRaw;
|
|
407
|
+
private lastFed;
|
|
408
|
+
protected readonly seed: number;
|
|
409
|
+
private readonly sampler;
|
|
410
|
+
private readonly tipTable;
|
|
411
|
+
/** The pen dab the marks being made belong to, and its gradient colour. */
|
|
412
|
+
private pen;
|
|
413
|
+
private penColor;
|
|
414
|
+
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
415
|
+
push(points: readonly PenPoint[]): RasterRect[];
|
|
416
|
+
private step;
|
|
417
|
+
/** The colour of a mark at (x, y): its pen dab's, or across the tip by its offset from the pen. */
|
|
418
|
+
private markColor;
|
|
419
|
+
finish(): RasterRect[];
|
|
420
|
+
/** One simulation step of copy `copy` with the pen at dab `d`. */
|
|
421
|
+
protected abstract advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
422
|
+
/** Stamps one round mark of radius `r` at (x, y) with flow `alpha` towards `opacity`. */
|
|
423
|
+
protected mark(x: number, y: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
424
|
+
/** Marks along a segment, spaced by a fraction of the radius. */
|
|
425
|
+
protected segment(x0: number, y0: number, x1: number, y1: number, r: number, alpha: number, opacity: number, pressure: number, rects: RasterRect[]): void;
|
|
426
|
+
}
|
|
427
|
+
type ParticleKind = 'string' | 'spring' | 'orbit' | 'flow';
|
|
428
|
+
declare const PARTICLE_KINDS: readonly ParticleKind[];
|
|
429
|
+
type ParticleRender = 'dot' | 'line' | 'sequence';
|
|
430
|
+
declare const PARTICLE_RENDERS: readonly ParticleRender[];
|
|
431
|
+
/** Particle engine settings (each 0..1 unless noted). */
|
|
432
|
+
interface ParticleSettings {
|
|
433
|
+
kind: ParticleKind;
|
|
434
|
+
render: ParticleRender;
|
|
435
|
+
/** Particles, 1..64. */
|
|
436
|
+
count: number;
|
|
437
|
+
/** Mark size as a fraction of the tip size. */
|
|
438
|
+
size: number;
|
|
439
|
+
/** How far apart the particles start, as a fraction of the tip size. */
|
|
440
|
+
spread: number;
|
|
441
|
+
/** Spring and string stiffness (stiff follows closely; soft swings wide). */
|
|
442
|
+
stiffness: number;
|
|
443
|
+
/** How quickly motion dies down. */
|
|
444
|
+
damping: number;
|
|
445
|
+
/** Orbit and flow speed. */
|
|
446
|
+
speed: number;
|
|
447
|
+
/** Pull downwards (−1 up .. 1 down). */
|
|
448
|
+
gravity: number;
|
|
449
|
+
/** Flow particles' lifetime. */
|
|
450
|
+
life: number;
|
|
451
|
+
/** Sequence: spacing of the beads along each path. */
|
|
452
|
+
interval: number;
|
|
453
|
+
}
|
|
454
|
+
declare const DEFAULT_PARTICLE: Readonly<ParticleSettings>;
|
|
455
|
+
declare function normalizeParticle(raw?: Partial<ParticleSettings>, base?: ParticleSettings): ParticleSettings;
|
|
456
|
+
/** A particle-brush stroke. */
|
|
457
|
+
declare class ParticleStroke extends MarkStroke {
|
|
458
|
+
private readonly sets;
|
|
459
|
+
private readonly s;
|
|
460
|
+
private spawn;
|
|
461
|
+
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
462
|
+
private init;
|
|
463
|
+
protected advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
464
|
+
}
|
|
465
|
+
/** Bristle engine settings (each 0..1 unless noted). */
|
|
466
|
+
interface BristleSettings {
|
|
467
|
+
/** Bristles in the tuft, 3..128. */
|
|
468
|
+
count: number;
|
|
469
|
+
/** Stiff bristles follow the tip; soft ones lag and bend into curves at turns. */
|
|
470
|
+
stiffness: number;
|
|
471
|
+
/** How far pressure spreads the tuft. */
|
|
472
|
+
splay: number;
|
|
473
|
+
/** Width of each bristle's line as a fraction of the tip size. */
|
|
474
|
+
thickness: number;
|
|
475
|
+
/** Paint per bristle: 1 never runs dry; lower, bristles run out along the stroke (dry brush). */
|
|
476
|
+
load: number;
|
|
477
|
+
/** Random variation between bristles (length, paint, position). */
|
|
478
|
+
jitter: number;
|
|
479
|
+
}
|
|
480
|
+
declare const DEFAULT_BRISTLE: Readonly<BristleSettings>;
|
|
481
|
+
declare function normalizeBristle(raw?: Partial<BristleSettings>, base?: BristleSettings): BristleSettings;
|
|
482
|
+
/** A bristle-brush stroke: every bristle draws its own line. */
|
|
483
|
+
declare class BristleStroke extends MarkStroke {
|
|
484
|
+
private readonly tufts;
|
|
485
|
+
private readonly s;
|
|
486
|
+
constructor(brush: PaintBrush, options: ProceduralStrokeOptions);
|
|
487
|
+
protected advance(copy: number, d: PenDab, rects: RasterRect[]): void;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Paint editor brushes: the shared engine's tip settings (`@pixerate/editor/paint`) plus what a
|
|
492
|
+
* natural-media brush adds: paper grain response, pen dynamics (pressure, tilt, barrel rotation,
|
|
493
|
+
* velocity), separate pressure smoothing, a rotation mode, jitter, and Mixer Brush settings for
|
|
494
|
+
* the blending brushes. The built-in presets are our own settings; none come from another product.
|
|
495
|
+
*/
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* How a brush makes marks: `dab` (dabs into a stroke buffer, or dab by dab through the pick-up /
|
|
499
|
+
* mix exchange when its behaviour asks for it), `eraser` (removes alpha), `mixer` (the Mixer
|
|
500
|
+
* Brush), `blender` (mixes the colours under and near the tip) or `smudge` (drags paint along).
|
|
501
|
+
*/
|
|
502
|
+
type PaintBrushEngine = 'dab' | 'eraser' | 'mixer' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker';
|
|
503
|
+
declare const PAINT_BRUSH_ENGINES: readonly PaintBrushEngine[];
|
|
504
|
+
type PaintBrushCategory = 'pencil' | 'pastel' | 'charcoal' | 'airbrush' | 'marker' | 'ink' | 'paint' | 'eraser' | 'blender' | 'smudge' | 'mixer' | 'watercolor' | 'wet-tool' | 'oil' | 'impasto' | 'procedural' | 'bristle' | 'sticker' | 'custom';
|
|
505
|
+
declare const PAINT_BRUSH_CATEGORIES: readonly PaintBrushCategory[];
|
|
506
|
+
/**
|
|
507
|
+
* What a wet brush does to the wet layer: `paint` (water and pigment), `water` (clear water;
|
|
508
|
+
* with `rewet` it lifts dried paint), `dry` (a hair dryer: takes water, the pigment settles),
|
|
509
|
+
* `blow` (pushes the water along the stroke), `blend` (evens out the wet pigment under the tip),
|
|
510
|
+
* `smudge` (drags wet paint along) or `blot` (a tissue: takes up wet paint).
|
|
511
|
+
*/
|
|
512
|
+
type WetTool = 'paint' | 'water' | 'dry' | 'blow' | 'blend' | 'smudge' | 'blot';
|
|
513
|
+
declare const WET_TOOLS: readonly WetTool[];
|
|
514
|
+
/**
|
|
515
|
+
* The wet medium a wet brush paints: its layer takes the medium's wet settings the first time it
|
|
516
|
+
* holds wet paint (`watercolor`: runny, dries in seconds, granulates and darkens at edges;
|
|
517
|
+
* `gouache`: opaque, flatter; `oil`: thick, barely flows, stays wet for a long time and blends into
|
|
518
|
+
* the wet paint it meets).
|
|
519
|
+
*/
|
|
520
|
+
type WetMediumKind = 'watercolor' | 'gouache' | 'oil';
|
|
521
|
+
declare const WET_MEDIA: readonly WetMediumKind[];
|
|
522
|
+
/** Wet engine settings, each 0..1. */
|
|
523
|
+
interface WetBrushSettings {
|
|
524
|
+
tool: WetTool;
|
|
525
|
+
/** Water the brush lays down (wetness): 0.2 a damp brush, 1 a dripping one. */
|
|
526
|
+
water: number;
|
|
527
|
+
/** Pigment concentration: 0 clear water .. 1 paint straight from the tube. */
|
|
528
|
+
pigment: number;
|
|
529
|
+
/** Body: 0 a transparent watercolour .. 1 opaque gouache that hides what is under it. */
|
|
530
|
+
body: number;
|
|
531
|
+
/** Strength of the dry, blow, blend, smudge and blot tools. */
|
|
532
|
+
strength: number;
|
|
533
|
+
/** How much dried paint the water lifts again (re-wetting): 0 none (staining) .. 1. */
|
|
534
|
+
rewet: number;
|
|
535
|
+
/** The medium (its layer's wet settings the first time it holds wet paint). */
|
|
536
|
+
medium: WetMediumKind;
|
|
537
|
+
/** Paint loading: 1 never runs out; lower, the brush runs dry along a long stroke (reload each stroke). */
|
|
538
|
+
load: number;
|
|
539
|
+
/** Thinner (water for watercolour, solvent for oil): thins the paint, so it is more transparent and runnier. */
|
|
540
|
+
thinner: number;
|
|
541
|
+
}
|
|
542
|
+
declare const DEFAULT_WET_BRUSH: Readonly<WetBrushSettings>;
|
|
543
|
+
declare function normalizeWetBrush(raw?: Partial<WetBrushSettings>, base?: WetBrushSettings): WetBrushSettings;
|
|
544
|
+
/**
|
|
545
|
+
* What an impasto brush does on a thick-paint layer: `brush` (paint with body: colour and relief),
|
|
546
|
+
* `tube` (squeezes blobs of paint straight from the tube), `knife` (a palette knife: scrapes the
|
|
547
|
+
* relief flat and smears the paint along), `roller` (a flat, even band with a stipple), `gloop`
|
|
548
|
+
* (a thick, glossy, rounded line).
|
|
549
|
+
*/
|
|
550
|
+
type ImpastoTool = 'brush' | 'tube' | 'knife' | 'roller' | 'gloop';
|
|
551
|
+
declare const IMPASTO_TOOLS: readonly ImpastoTool[];
|
|
552
|
+
/** Impasto engine settings, each 0..1. */
|
|
553
|
+
interface ImpastoSettings {
|
|
554
|
+
tool: ImpastoTool;
|
|
555
|
+
/** How thick the paint stands up (relief per stroke). */
|
|
556
|
+
thickness: number;
|
|
557
|
+
/** Shine of the paint when lit (0 matte .. 1 wet-looking gloss). */
|
|
558
|
+
gloss: number;
|
|
559
|
+
/** Metallic paint: highlights take the paint's colour (gold, copper, silver). */
|
|
560
|
+
metallic: number;
|
|
561
|
+
/** Paint loading: 1 never runs out; lower, the stroke thins and fades as the brush empties. */
|
|
562
|
+
load: number;
|
|
563
|
+
/** Thinner: flatter, more transparent paint that levels out. */
|
|
564
|
+
thinner: number;
|
|
565
|
+
/** Knife and brush: how much of the paint under the tool is dragged along (0..1). */
|
|
566
|
+
drag: number;
|
|
567
|
+
}
|
|
568
|
+
declare const DEFAULT_IMPASTO: Readonly<ImpastoSettings>;
|
|
569
|
+
declare function normalizeImpasto(raw?: Partial<ImpastoSettings>, base?: ImpastoSettings): ImpastoSettings;
|
|
570
|
+
/** Multi-coloured brush: `tip` lays the colours across the tip, `dab` changes colour from dab to dab. */
|
|
571
|
+
type MultiColorMode = 'off' | 'tip' | 'dab';
|
|
572
|
+
declare const MULTI_COLOR_MODES: readonly MultiColorMode[];
|
|
573
|
+
interface MultiColorSettings {
|
|
574
|
+
/** `off` (default), `tip` (the colours side by side across the tip, like a brush loaded with several paints; the stripes follow the stroke) or `dab` (the colour changes along the stroke). */
|
|
575
|
+
mode: MultiColorMode;
|
|
576
|
+
/** 2..8 colours #rrggbb. Fewer than 2: the paint colour and the secondary colour. */
|
|
577
|
+
colors: string[];
|
|
578
|
+
/** `dab` mode: `sequence` (default) runs through the colours in order, blending between them; `random` picks one per run of dabs (seeded, so replays match). */
|
|
579
|
+
order: 'sequence' | 'random';
|
|
580
|
+
/** `dab` mode: dabs per colour, 1..500 (default 12). */
|
|
581
|
+
length: number;
|
|
582
|
+
/** How softly neighbouring colours blend, 0 (hard stripes or steps) .. 1 (smooth gradient, default). */
|
|
583
|
+
softness: number;
|
|
584
|
+
}
|
|
585
|
+
declare const DEFAULT_MULTI_COLOR: Readonly<MultiColorSettings>;
|
|
586
|
+
/**
|
|
587
|
+
* How a brush handles paint on the canvas. The pick-up / mix ratios are the shared exchange's
|
|
588
|
+
* (`@pixerate/editor/paint` `PaintExchange`); wet media will use the same ones. On a Mixer Brush
|
|
589
|
+
* they are its `mixer.wet`, `mixer.mix`, `!mixer.cleanEachStroke` and `mixer.mixing`, kept in sync.
|
|
590
|
+
*/
|
|
591
|
+
interface BrushBehavior {
|
|
592
|
+
/** Pick-up ratio, 0..1: how much of the paint under each dab the brush lifts and carries (0: none, default). */
|
|
593
|
+
pickup: number;
|
|
594
|
+
/** Mix ratio, 0..1: how much of the carried paint goes into what the brush lays down, against its own colour (default 0.5). */
|
|
595
|
+
mix: number;
|
|
596
|
+
/** Dirty brush: keep the carried paint after the stroke, so the next stroke starts with the last colour the brush touched (default false). */
|
|
597
|
+
dirty: boolean;
|
|
598
|
+
/** How picked-up paint, multi-colour gradients and the deposit mix: `pigment` (default, Kubelka-Munk) or `rgb`. */
|
|
599
|
+
mixing: MixingModel;
|
|
600
|
+
/** Multi-coloured brush. */
|
|
601
|
+
multicolor: MultiColorSettings;
|
|
602
|
+
}
|
|
603
|
+
declare const DEFAULT_BEHAVIOR: Readonly<BrushBehavior>;
|
|
604
|
+
/** What turns the tip: a fixed angle, the direction of the stroke, or the pen's barrel rotation. */
|
|
605
|
+
type BrushRotation = 'fixed' | 'stroke' | 'barrel';
|
|
606
|
+
declare const BRUSH_ROTATIONS: readonly BrushRotation[];
|
|
607
|
+
/** Pen dynamics and jitter. Every amount is 0 (off) .. 1 unless noted. */
|
|
608
|
+
interface BrushDynamics {
|
|
609
|
+
/** Size at zero pressure as a fraction of the full size, when pressure controls size (default 0). */
|
|
610
|
+
minSize: number;
|
|
611
|
+
/** How much pressure scales flow (default 0). */
|
|
612
|
+
pressureFlow: number;
|
|
613
|
+
/** Laying the pen down widens the tip, up to 2.5× (default 0). */
|
|
614
|
+
tiltSize: number;
|
|
615
|
+
/** Laying the pen down flattens the tip along the tilt direction (default 0). */
|
|
616
|
+
tiltRoundness: number;
|
|
617
|
+
/** Laying the pen down lightens the mark, as when shading with the side of a pencil (default 0). */
|
|
618
|
+
tiltOpacity: number;
|
|
619
|
+
/** -1..1: positive grows the tip when the pen moves fast, negative thins it (default 0). */
|
|
620
|
+
velocitySize: number;
|
|
621
|
+
/** -1..1: positive darkens fast marks, negative lightens them (default 0). */
|
|
622
|
+
velocityOpacity: number;
|
|
623
|
+
/** Random size change per dab (default 0). */
|
|
624
|
+
sizeJitter: number;
|
|
625
|
+
/** Random angle per dab, as a fraction of ±180° (default 0). */
|
|
626
|
+
angleJitter: number;
|
|
627
|
+
/** Random opacity drop per dab (default 0). */
|
|
628
|
+
opacityJitter: number;
|
|
629
|
+
/** Random offset across the stroke in diameters, 0..4 (default 0). */
|
|
630
|
+
scatter: number;
|
|
631
|
+
}
|
|
632
|
+
declare const DEFAULT_DYNAMICS: Readonly<BrushDynamics>;
|
|
633
|
+
interface PaintBrush {
|
|
634
|
+
/** Library id (lowercase letters, digits and dashes). */
|
|
635
|
+
id: string;
|
|
636
|
+
name: string;
|
|
637
|
+
category: PaintBrushCategory;
|
|
638
|
+
engine: PaintBrushEngine;
|
|
639
|
+
/** The shared engine's tip: size, hardness, spacing, angle, roundness, opacity, flow, position smoothing, pressure to size / opacity, blend. */
|
|
640
|
+
tip: BrushSettings;
|
|
641
|
+
/** How strongly the mark follows the paper grain, 0 (ignores it) .. 1 (catches only the peaks at light pressure). */
|
|
642
|
+
grain: number;
|
|
643
|
+
/** Pressure smoothing 0..0.95, separate from the tip's position smoothing (default 0). */
|
|
644
|
+
pressureSmoothing: number;
|
|
645
|
+
rotation: BrushRotation;
|
|
646
|
+
dynamics: BrushDynamics;
|
|
647
|
+
/** Mixer engine only: the Mixer Brush settings. */
|
|
648
|
+
mixer: MixerSettings;
|
|
649
|
+
/** Mixer engine only: load the current colour each stroke (false: a smudge or blender that only moves paint). */
|
|
650
|
+
loadsColor: boolean;
|
|
651
|
+
/** Pick-up and mix ratios, dirty brush and multi-colour (dab and mixer engines). */
|
|
652
|
+
behavior: BrushBehavior;
|
|
653
|
+
/** Blender engine only: strength, blend distance and mixing. */
|
|
654
|
+
blender: BlenderSettings;
|
|
655
|
+
/** Smudge engine only: strength, length and mixing. */
|
|
656
|
+
smudge: SmudgeSettings;
|
|
657
|
+
/** Wet engine only: the wet tool, water, pigment, body and strength. */
|
|
658
|
+
wet: WetBrushSettings;
|
|
659
|
+
/** Impasto engine only: the tool, thickness, gloss, metallic, loading, thinner and drag. */
|
|
660
|
+
impasto: ImpastoSettings;
|
|
661
|
+
/** Particle engine only: the particles' kind, how they are drawn, and their physics. */
|
|
662
|
+
particle: ParticleSettings;
|
|
663
|
+
/** Bristle engine only: the tuft. */
|
|
664
|
+
bristle: BristleSettings;
|
|
665
|
+
/** Sticker engine only: the stamps and how they are sprayed. */
|
|
666
|
+
sticker: StickerSettings;
|
|
667
|
+
/**
|
|
668
|
+
* The brush's own gradient paint (`@pixerate/editor/paint` `PaintGradientSource`), used instead
|
|
669
|
+
* of the editor's paint; `null` (default): the editor's paint. Stops may be `paint` / `secondary`
|
|
670
|
+
* (the editor's two colours). This is what the multi-colour brush became.
|
|
671
|
+
*/
|
|
672
|
+
paint: PaintGradientSource | null;
|
|
673
|
+
/** True for the built-in presets (they cannot be removed or overwritten). */
|
|
674
|
+
builtin?: boolean;
|
|
675
|
+
}
|
|
676
|
+
declare function normalizeDynamics(raw?: Partial<BrushDynamics>, base?: BrushDynamics): BrushDynamics;
|
|
677
|
+
declare const BRUSH_ID: RegExp;
|
|
678
|
+
declare function normalizeMultiColor(raw?: Partial<MultiColorSettings>, base?: MultiColorSettings): MultiColorSettings;
|
|
679
|
+
/** Behaviour input: any subset, with a partial multi-colour block. */
|
|
680
|
+
type BrushBehaviorInput = Partial<Omit<BrushBehavior, 'multicolor'>> & {
|
|
681
|
+
multicolor?: Partial<MultiColorSettings>;
|
|
682
|
+
};
|
|
683
|
+
declare function normalizeBehavior(raw?: BrushBehaviorInput, base?: BrushBehavior): BrushBehavior;
|
|
684
|
+
/** Brush settings to change or read: any subset of a brush and of its blocks. */
|
|
685
|
+
type PaintBrushInput = Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer' | 'behavior' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker' | 'paint'>> & {
|
|
686
|
+
/** A gradient (or `null` for the editor's paint); a plain colour source clears it. */
|
|
687
|
+
paint?: PaintSource | null;
|
|
688
|
+
tip?: Partial<BrushSettings>;
|
|
689
|
+
dynamics?: Partial<BrushDynamics>;
|
|
690
|
+
mixer?: Partial<MixerSettings>;
|
|
691
|
+
behavior?: BrushBehaviorInput;
|
|
692
|
+
blender?: Partial<BlenderSettings>;
|
|
693
|
+
smudge?: Partial<SmudgeSettings>;
|
|
694
|
+
wet?: Partial<WetBrushSettings>;
|
|
695
|
+
impasto?: Partial<ImpastoSettings>;
|
|
696
|
+
particle?: Partial<ParticleSettings>;
|
|
697
|
+
bristle?: Partial<BristleSettings>;
|
|
698
|
+
sticker?: Partial<StickerSettings>;
|
|
699
|
+
};
|
|
700
|
+
/** Whether a brush can pick paint up from the canvas (the pick-up / mix ratios apply): dab, mixer and wet brushes. */
|
|
701
|
+
declare function isWetCapable(brush: Pick<PaintBrush, 'engine'>): boolean;
|
|
702
|
+
/** Whether a brush paints wet media (on the layer's wet layer). */
|
|
703
|
+
declare function isWetBrush(brush: Pick<PaintBrush, 'engine'>): boolean;
|
|
704
|
+
/**
|
|
705
|
+
* Whether a dab brush paints dab by dab through the exchange (it picks up paint or paints a
|
|
706
|
+
* gradient) instead of building one coverage buffer of one colour.
|
|
707
|
+
*/
|
|
708
|
+
declare function usesPaintExchange(brush: PaintBrush, gradient?: boolean): boolean;
|
|
709
|
+
/** Whether strokes of a brush paint colour, so gradient paint applies (not erasers, blenders, smudges or the wet tools other than paint). */
|
|
710
|
+
declare function brushTakesPaint(brush: Pick<PaintBrush, 'engine' | 'loadsColor' | 'wet'>): boolean;
|
|
711
|
+
/**
|
|
712
|
+
* The multi-colour settings a gradient paints the same as (the inverse of `multiColorToPaint`), or
|
|
713
|
+
* `null` when the gradient has no multi-colour equivalent. Brushes report it as
|
|
714
|
+
* `behavior.multicolor`, so older readers keep working.
|
|
715
|
+
*/
|
|
716
|
+
declare function paintToMultiColor(g: PaintGradientSource | null): MultiColorSettings | null;
|
|
717
|
+
/**
|
|
718
|
+
* The gradient paint a multi-coloured brush (`behavior.multicolor`, before gradient paint) paints
|
|
719
|
+
* the same pixels with: `tip` → `across-tip`; `dab` in sequence → `distance` in dabs, repeating
|
|
720
|
+
* through the colours and back to the first; `dab` at random → `random` per run of dabs, with hard
|
|
721
|
+
* stops. Without 2 colours of its own it uses the editor's paint and second colours.
|
|
722
|
+
*/
|
|
723
|
+
declare function multiColorToPaint(mc: MultiColorSettings, mixing: MixingModel): PaintGradientSource | null;
|
|
724
|
+
/** A brush with every field in range; missing fields come from `base` (default: a plain round dab brush). */
|
|
725
|
+
declare function normalizePaintBrush(raw?: PaintBrushInput, base?: PaintBrush): PaintBrush;
|
|
726
|
+
/** The built-in brush library. */
|
|
727
|
+
declare const BUILTIN_BRUSHES: readonly PaintBrush[];
|
|
728
|
+
declare const DEFAULT_BRUSH_ID = "pencil-6b";
|
|
729
|
+
|
|
730
|
+
/** A brush as plain JSON (for export, agents and saved libraries): no `builtin` flag. */
|
|
731
|
+
declare function brushToJSON(brush: PaintBrush): Omit<PaintBrush, 'builtin'>;
|
|
732
|
+
/** The JSON file a brush library exports to and imports from. */
|
|
733
|
+
interface BrushLibraryFile {
|
|
734
|
+
format: 'pixerate-brushes';
|
|
735
|
+
version: 1;
|
|
736
|
+
brushes: Array<Omit<PaintBrush, 'builtin'>>;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Brushes read from a brush library file (or a bare array of brushes). Each is normalised; ones
|
|
740
|
+
* without a valid id or name are reported in `problems` and skipped.
|
|
741
|
+
*/
|
|
742
|
+
declare function parseBrushLibrary(raw: unknown, problems?: string[]): PaintBrush[];
|
|
743
|
+
|
|
744
|
+
export { type PaintBrushCategory as $, DEFAULT_WET_BRUSH as A, type BrushLibraryFile as B, type CoverageStroke as C, DEFAULT_BEHAVIOR as D, DryStroke as E, type DryStrokeOptions as F, GRAIN_PATTERNS as G, GRAIN_TILE as H, type ImpastoTool as I, type GrainPattern as J, type GrainTuning as K, IDENTITY_COPY as L, type MultiColorSettings as M, IMPASTO_TOOLS as N, type ImpastoSettings as O, type PaintBrush as P, type Linear2 as Q, MULTI_COLOR_MODES as R, type StrokeCopy as S, type MultiColorMode as T, PAINT_BRUSH_CATEGORIES as U, PAINT_BRUSH_ENGINES as V, type WetMediumKind as W, PAPER_FAMILIES as X, PAPER_LIBRARY as Y, PARTICLE_KINDS as Z, PARTICLE_RENDERS as _, type PenPoint as a, type PaintBrushEngine as a0, type PaperFamily as a1, type PaperTiling as a2, type ParticleSettings as a3, ParticleStroke as a4, type PenDab as a5, PenDabWalker as a6, PenSmoother as a7, type ProceduralStrokeOptions as a8, STAMP_KINDS as a9, normalizeStickerUpdate as aA, normalizeWetBrush as aB, paintToMultiColor as aC, paperPreset as aD, parseBrushLibrary as aE, penOrientation as aF, renderPaper as aG, renderStickers as aH, stampCoverage as aI, stickerRect as aJ, stickerSurface as aK, tiltToOrientation as aL, toStrokePoint as aM, usesPaintExchange as aN, type StickerSettings as aa, StickerSpray as ab, type StickerSprayInit as ac, WET_MEDIA as ad, WET_TOOLS as ae, type WetBrushSettings as af, brushTakesPaint as ag, brushToJSON as ah, clonePaper as ai, drawSticker as aj, grainCatch as ak, grainHeights as al, hexToRgb8 as am, isWetBrush as an, isWetCapable as ao, mixerTip as ap, multiColorToPaint as aq, normalizeBehavior as ar, normalizeBristle as as, normalizeDynamics as at, normalizeImpasto as au, normalizeMultiColor as av, normalizePaintBrush as aw, normalizePaper as ax, normalizeParticle as ay, normalizeStickerSettings as az, PaperGrain as b, type PaperSettings as c, type Sticker as d, type PaintBrushInput as e, type ParticleKind as f, type ParticleRender as g, type StampKind as h, type WetTool as i, BRUSH_ID as j, BRUSH_ROTATIONS as k, BUILTIN_BRUSHES as l, type BristleSettings as m, BristleStroke as n, type BrushBehavior as o, type BrushBehaviorInput as p, type BrushDynamics as q, type BrushRotation as r, DEFAULT_BRISTLE as s, DEFAULT_BRUSH_ID as t, DEFAULT_DYNAMICS as u, DEFAULT_IMPASTO as v, DEFAULT_MULTI_COLOR as w, DEFAULT_PAPER as x, DEFAULT_PARTICLE as y, DEFAULT_STICKER as z };
|