@pixerate/editor 0.265.0 → 0.266.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/agent/index.cjs +4 -3
  2. package/dist/agent/index.js +3 -2
  3. package/dist/animation-editor/index.cjs +59 -59
  4. package/dist/animation-editor/index.js +3 -3
  5. package/dist/brushes/index.cjs +56 -56
  6. package/dist/brushes/index.js +3 -3
  7. package/dist/brushes/presets.d.cts +3 -3
  8. package/dist/brushes/presets.d.ts +3 -3
  9. package/dist/brushes/resolve.d.cts +1 -1
  10. package/dist/brushes/resolve.d.ts +1 -1
  11. package/dist/{brushstrokeRender-JQ2XQRUD.cjs → brushstrokeRender-FSUWXUZY.cjs} +15 -15
  12. package/dist/{brushstrokeRender-4UB6NRJW.js → brushstrokeRender-KD453OBX.js} +3 -3
  13. package/dist/canvas/index.cjs +15 -14
  14. package/dist/canvas/index.js +3 -2
  15. package/dist/{chunk-Q6RVVJW6.js → chunk-3C33YTNN.js} +1 -1
  16. package/dist/{chunk-NOP4Y5AB.cjs → chunk-3SBLVGOM.cjs} +23 -23
  17. package/dist/{chunk-TMDDMJCR.js → chunk-472INMWH.js} +4 -2
  18. package/dist/{chunk-ZS7LB2MV.js → chunk-4ODRXQRW.js} +1 -1
  19. package/dist/chunk-55ALHYJY.js +289 -0
  20. package/dist/{chunk-NHA26O5X.cjs → chunk-7KNW3WXZ.cjs} +14 -12
  21. package/dist/{chunk-O53MOGXG.cjs → chunk-AQZQWXEU.cjs} +35 -35
  22. package/dist/{chunk-J4SGJCPW.js → chunk-CIJBSUM5.js} +1133 -1806
  23. package/dist/{chunk-XDNWQMHY.js → chunk-DXYHQ2VN.js} +1 -1
  24. package/dist/chunk-E2Z4UARJ.cjs +289 -0
  25. package/dist/{chunk-BLR4KWYQ.js → chunk-EZOJBP4D.js} +1 -1
  26. package/dist/chunk-HAEB3MZD.js +664 -0
  27. package/dist/chunk-KIJIONZF.cjs +664 -0
  28. package/dist/{chunk-Z2Z2OHRM.js → chunk-LK4SXL3V.js} +1 -1
  29. package/dist/{chunk-DGXTEPCU.cjs → chunk-MVUQSK5J.cjs} +86 -86
  30. package/dist/{chunk-LPFQHVCO.js → chunk-PXXXRCME.js} +1 -284
  31. package/dist/{chunk-T7QSEKDY.cjs → chunk-QJVOJUTD.cjs} +4 -287
  32. package/dist/{chunk-WY23AUCA.js → chunk-R44Y3EFS.js} +1 -1
  33. package/dist/{chunk-677777GH.cjs → chunk-RFJZGTHA.cjs} +1137 -1810
  34. package/dist/{chunk-7PW6VCNW.js → chunk-T2LDTEXK.js} +1 -1
  35. package/dist/{chunk-7NQOGWLM.cjs → chunk-TOID45I2.cjs} +1 -1
  36. package/dist/{chunk-DEBIFWY6.cjs → chunk-VRK322ZC.cjs} +2 -2
  37. package/dist/{chunk-5OE4ZZ6V.cjs → chunk-WLUVZKVD.cjs} +30 -30
  38. package/dist/{chunk-ZHRVCW3M.cjs → chunk-XRXFVRGY.cjs} +10 -10
  39. package/dist/commands/index.cjs +26 -656
  40. package/dist/commands/index.js +18 -648
  41. package/dist/image-editor/index.cjs +786 -786
  42. package/dist/image-editor/index.js +4 -4
  43. package/dist/paint-editor/brush-model.d.cts +273 -0
  44. package/dist/paint-editor/brush-model.d.ts +273 -0
  45. package/dist/paint-editor/brushes.d.cts +6 -270
  46. package/dist/paint-editor/brushes.d.ts +6 -270
  47. package/dist/paint-editor/index.cjs +79 -79
  48. package/dist/paint-editor/index.js +5 -5
  49. package/dist/paint-editor/ui.d.cts +1 -1
  50. package/dist/paint-editor/ui.d.ts +1 -1
  51. package/dist/vector/contract.d.cts +52 -0
  52. package/dist/vector/contract.d.ts +52 -0
  53. package/dist/vector/index.cjs +1167 -969
  54. package/dist/vector/index.d.cts +1 -0
  55. package/dist/vector/index.d.ts +1 -0
  56. package/dist/vector/index.js +203 -5
  57. package/package.json +1 -1
@@ -8,7 +8,7 @@ import {
8
8
  parseOpenTypeFont,
9
9
  removeAnchorsKeepingShape,
10
10
  traceField
11
- } from "../chunk-BLR4KWYQ.js";
11
+ } from "../chunk-EZOJBP4D.js";
12
12
  import {
13
13
  ADJUSTMENT_KEYS,
14
14
  ADJUSTMENT_KINDS,
@@ -410,7 +410,7 @@ import {
410
410
  writePsd,
411
411
  xyToTempTint,
412
412
  xyToUv
413
- } from "../chunk-WY23AUCA.js";
413
+ } from "../chunk-R44Y3EFS.js";
414
414
  import {
415
415
  ColorGmm,
416
416
  DEFAULT_REFINE_EDGE_PARAMS,
@@ -770,14 +770,14 @@ import {
770
770
  warpMeshParamAt,
771
771
  warpMeshRows,
772
772
  warpPresetMesh
773
- } from "../chunk-7PW6VCNW.js";
773
+ } from "../chunk-T2LDTEXK.js";
774
774
  import {
775
775
  IMAGE_PROFILE,
776
776
  checkBrush,
777
777
  closest,
778
778
  createBrushLibrary,
779
779
  resolveBrush
780
- } from "../chunk-J4SGJCPW.js";
780
+ } from "../chunk-CIJBSUM5.js";
781
781
  import "../chunk-ACFSFUQH.js";
782
782
  import "../chunk-M6DNZNVY.js";
783
783
  import {
@@ -0,0 +1,273 @@
1
+ /**
2
+ * The flat engine brush (`PaintBrush`) the paint engine reads, its settings and
3
+ * `normalizePaintBrush`. Kept apart from the built-in library (`./brushes`) so the facet
4
+ * resolver (`brushes/resolve`) can import it while `BUILTIN_BRUSHES` resolves the facet presets.
5
+ */
6
+ import { type BrushSettings } from '../paint/brush.cjs';
7
+ import { type BlenderSettings, type SmudgeSettings } from '../paint/blending.cjs';
8
+ import type { MixingModel } from '../paint/exchange.cjs';
9
+ import { type PaintGradientSource, type PaintSource } from '../paint/gradient-paint.cjs';
10
+ import { type MixerSettings } from '../paint/mixer.cjs';
11
+ import { type BristleSettings, type ParticleSettings, type StickerSettings } from './mark-settings.cjs';
12
+ /**
13
+ * How a brush makes marks: `dab` (dabs into a stroke buffer, or dab by dab through the pick-up /
14
+ * mix exchange when its behaviour asks for it), `eraser` (removes alpha), `mixer` (the Mixer
15
+ * Brush), `blender` (mixes the colours under and near the tip) or `smudge` (drags paint along).
16
+ */
17
+ export type PaintBrushEngine = 'dab' | 'eraser' | 'mixer' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker';
18
+ export declare const PAINT_BRUSH_ENGINES: readonly PaintBrushEngine[];
19
+ export type PaintBrushCategory = 'pencil' | 'pastel' | 'charcoal' | 'airbrush' | 'marker' | 'ink' | 'paint' | 'eraser' | 'blender' | 'smudge' | 'mixer' | 'watercolor' | 'wet-tool' | 'oil' | 'impasto' | 'procedural' | 'bristle' | 'sticker' | 'light' | 'custom';
20
+ export declare const PAINT_BRUSH_CATEGORIES: readonly PaintBrushCategory[];
21
+ /**
22
+ * What a wet brush does to the wet layer: `paint` (water and pigment), `water` (clear water;
23
+ * with `rewet` it lifts dried paint), `dry` (a hair dryer: takes water, the pigment settles),
24
+ * `blow` (pushes the water along the stroke), `blend` (evens out the wet pigment under the tip),
25
+ * `smudge` (drags wet paint along) or `blot` (a tissue: takes up wet paint).
26
+ */
27
+ export type WetTool = 'paint' | 'water' | 'dry' | 'blow' | 'blend' | 'smudge' | 'blot';
28
+ export declare const WET_TOOLS: readonly WetTool[];
29
+ /**
30
+ * The wet medium a wet brush paints: its layer takes the medium's wet settings the first time it
31
+ * holds wet paint (`watercolor`: runny, dries in seconds, granulates and darkens at edges;
32
+ * `gouache`: opaque, flatter; `oil`: thick, barely flows, stays wet for a long time and blends into
33
+ * the wet paint it meets).
34
+ */
35
+ export type WetMediumKind = 'watercolor' | 'gouache' | 'oil';
36
+ export declare const WET_MEDIA: readonly WetMediumKind[];
37
+ /** Wet engine settings, each 0..1. */
38
+ export interface WetBrushSettings {
39
+ tool: WetTool;
40
+ /** Water the brush lays down (wetness): 0.2 a damp brush, 1 a dripping one. */
41
+ water: number;
42
+ /** Pigment concentration: 0 clear water .. 1 paint straight from the tube. */
43
+ pigment: number;
44
+ /** Body: 0 a transparent watercolour .. 1 opaque gouache that hides what is under it. */
45
+ body: number;
46
+ /** Strength of the dry, blow, blend, smudge and blot tools. */
47
+ strength: number;
48
+ /** How much dried paint the water lifts again (re-wetting): 0 none (staining) .. 1. */
49
+ rewet: number;
50
+ /** The medium (its layer's wet settings the first time it holds wet paint). */
51
+ medium: WetMediumKind;
52
+ /** Paint loading: 1 never runs out; lower, the brush runs dry along a long stroke (reload each stroke). */
53
+ load: number;
54
+ /** Thinner (water for watercolour, solvent for oil): thins the paint, so it is more transparent and runnier. */
55
+ thinner: number;
56
+ }
57
+ export declare const DEFAULT_WET_BRUSH: Readonly<WetBrushSettings>;
58
+ export declare function normalizeWetBrush(raw?: Partial<WetBrushSettings>, base?: WetBrushSettings): WetBrushSettings;
59
+ /**
60
+ * What an impasto brush does on a thick-paint layer: `brush` (paint with body: colour and relief),
61
+ * `tube` (squeezes blobs of paint straight from the tube), `knife` (a palette knife: scrapes the
62
+ * relief flat and smears the paint along), `roller` (a flat, even band with a stipple), `gloop`
63
+ * (a thick, glossy, rounded line).
64
+ */
65
+ export type ImpastoTool = 'brush' | 'tube' | 'knife' | 'roller' | 'gloop';
66
+ export declare const IMPASTO_TOOLS: readonly ImpastoTool[];
67
+ /** Impasto engine settings, each 0..1. */
68
+ export interface ImpastoSettings {
69
+ tool: ImpastoTool;
70
+ /** How thick the paint stands up (relief per stroke). */
71
+ thickness: number;
72
+ /** Shine of the paint when lit (0 matte .. 1 wet-looking gloss). */
73
+ gloss: number;
74
+ /** Metallic paint: highlights take the paint's colour (gold, copper, silver). */
75
+ metallic: number;
76
+ /** Paint loading: 1 never runs out; lower, the stroke thins and fades as the brush empties. */
77
+ load: number;
78
+ /** Thinner: flatter, more transparent paint that levels out. */
79
+ thinner: number;
80
+ /** Knife and brush: how much of the paint under the tool is dragged along (0..1). */
81
+ drag: number;
82
+ /** Light paint: the bead's light is stronger where its surface turns away, as on backlit glass (0..1, default 0). */
83
+ rim: number;
84
+ /** Light paint: the middle of a bead lets the layers below show through, its edges stay solid (0..1, default 0). */
85
+ clarity: number;
86
+ }
87
+ export declare const DEFAULT_IMPASTO: Readonly<ImpastoSettings>;
88
+ /** Light paint: how much light a brush lays with its colour (see `doc.light`). */
89
+ export interface LightSettings {
90
+ /** Light each dab lays, scaled by its coverage and flow (0..1, default 0: none). */
91
+ amount: number;
92
+ /** Pressure scales the light (default false). */
93
+ pressure: boolean;
94
+ }
95
+ export declare const DEFAULT_LIGHT: Readonly<LightSettings>;
96
+ export declare function normalizeLight(raw?: Partial<LightSettings>, base?: LightSettings): LightSettings;
97
+ export declare function normalizeImpasto(raw?: Partial<ImpastoSettings>, base?: ImpastoSettings): ImpastoSettings;
98
+ /** Multi-coloured brush: `tip` lays the colours across the tip, `dab` changes colour from dab to dab. */
99
+ export type MultiColorMode = 'off' | 'tip' | 'dab';
100
+ export declare const MULTI_COLOR_MODES: readonly MultiColorMode[];
101
+ export interface MultiColorSettings {
102
+ /** `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). */
103
+ mode: MultiColorMode;
104
+ /** 2..8 colours #rrggbb. Fewer than 2: the paint colour and the secondary colour. */
105
+ colors: string[];
106
+ /** `dab` mode: `sequence` (default) runs through the colours in order, blending between them; `random` picks one per run of dabs (seeded, so replays match). */
107
+ order: 'sequence' | 'random';
108
+ /** `dab` mode: dabs per colour, 1..500 (default 12). */
109
+ length: number;
110
+ /** How softly neighbouring colours blend, 0 (hard stripes or steps) .. 1 (smooth gradient, default). */
111
+ softness: number;
112
+ }
113
+ export declare const DEFAULT_MULTI_COLOR: Readonly<MultiColorSettings>;
114
+ /**
115
+ * How a brush handles paint on the canvas. The pick-up / mix ratios are the shared exchange's
116
+ * (`@pixerate/editor/paint` `PaintExchange`); wet media will use the same ones. On a Mixer Brush
117
+ * they are its `mixer.wet`, `mixer.mix`, `!mixer.cleanEachStroke` and `mixer.mixing`, kept in sync.
118
+ */
119
+ export interface BrushBehavior {
120
+ /** Pick-up ratio, 0..1: how much of the paint under each dab the brush lifts and carries (0: none, default). */
121
+ pickup: number;
122
+ /** Mix ratio, 0..1: how much of the carried paint goes into what the brush lays down, against its own colour (default 0.5). */
123
+ mix: number;
124
+ /** Dirty brush: keep the carried paint after the stroke, so the next stroke starts with the last colour the brush touched (default false). */
125
+ dirty: boolean;
126
+ /** How picked-up paint, multi-colour gradients and the deposit mix: `pigment` (default, Kubelka-Munk) or `rgb`. */
127
+ mixing: MixingModel;
128
+ /** Multi-coloured brush. */
129
+ multicolor: MultiColorSettings;
130
+ }
131
+ export declare const DEFAULT_BEHAVIOR: Readonly<BrushBehavior>;
132
+ /** What turns the tip: a fixed angle, the direction of the stroke, or the pen's barrel rotation. */
133
+ export type BrushRotation = 'fixed' | 'stroke' | 'barrel';
134
+ export declare const BRUSH_ROTATIONS: readonly BrushRotation[];
135
+ /** Pen dynamics and jitter. Every amount is 0 (off) .. 1 unless noted. */
136
+ export interface BrushDynamics {
137
+ /** Size at zero pressure as a fraction of the full size, when pressure controls size (default 0). */
138
+ minSize: number;
139
+ /** How much pressure scales flow (default 0). */
140
+ pressureFlow: number;
141
+ /** Laying the pen down widens the tip, up to 2.5× (default 0). */
142
+ tiltSize: number;
143
+ /** Laying the pen down flattens the tip along the tilt direction (default 0). */
144
+ tiltRoundness: number;
145
+ /** Laying the pen down lightens the mark, as when shading with the side of a pencil (default 0). */
146
+ tiltOpacity: number;
147
+ /** -1..1: positive grows the tip when the pen moves fast, negative thins it (default 0). */
148
+ velocitySize: number;
149
+ /** -1..1: positive darkens fast marks, negative lightens them (default 0). */
150
+ velocityOpacity: number;
151
+ /** Random size change per dab (default 0). */
152
+ sizeJitter: number;
153
+ /** Random angle per dab, as a fraction of ±180° (default 0). */
154
+ angleJitter: number;
155
+ /** Random opacity drop per dab (default 0). */
156
+ opacityJitter: number;
157
+ /** Random offset across the stroke in diameters, 0..4 (default 0). */
158
+ scatter: number;
159
+ }
160
+ export declare const DEFAULT_DYNAMICS: Readonly<BrushDynamics>;
161
+ export interface PaintBrush {
162
+ /** Library id (lowercase letters, digits and dashes). */
163
+ id: string;
164
+ name: string;
165
+ category: PaintBrushCategory;
166
+ engine: PaintBrushEngine;
167
+ /** The shared engine's tip: size, hardness, spacing, angle, roundness, opacity, flow, position smoothing, pressure to size / opacity, blend. */
168
+ tip: BrushSettings;
169
+ /** How strongly the mark follows the paper grain, 0 (ignores it) .. 1 (catches only the peaks at light pressure). */
170
+ grain: number;
171
+ /** Pressure smoothing 0..0.95, separate from the tip's position smoothing (default 0). */
172
+ pressureSmoothing: number;
173
+ rotation: BrushRotation;
174
+ dynamics: BrushDynamics;
175
+ /** Mixer engine only: the Mixer Brush settings. */
176
+ mixer: MixerSettings;
177
+ /** Mixer engine only: load the current colour each stroke (false: a smudge or blender that only moves paint). */
178
+ loadsColor: boolean;
179
+ /** Pick-up and mix ratios, dirty brush and multi-colour (dab and mixer engines). */
180
+ behavior: BrushBehavior;
181
+ /** Blender engine only: strength, blend distance and mixing. */
182
+ blender: BlenderSettings;
183
+ /** Smudge engine only: strength, length and mixing. */
184
+ smudge: SmudgeSettings;
185
+ /** Wet engine only: the wet tool, water, pigment, body and strength. */
186
+ wet: WetBrushSettings;
187
+ /** Impasto engine only: the tool, thickness, gloss, metallic, loading, thinner and drag. */
188
+ impasto: ImpastoSettings;
189
+ /** Particle engine only: the particles' kind, how they are drawn, and their physics. */
190
+ particle: ParticleSettings;
191
+ /** Bristle engine only: the tuft. */
192
+ bristle: BristleSettings;
193
+ /** Sticker engine only: the stamps and how they are sprayed. */
194
+ sticker: StickerSettings;
195
+ /** Light paint: the light it lays with its colour (amount 0: none). */
196
+ light: LightSettings;
197
+ /**
198
+ * Dab engine: a stamp tip. Each dab (and each spray droplet) is this sticker's shape, an editor
199
+ * stamp (`editor:star`) or any sticker ref from the sticker library, laid in the paint colour;
200
+ * `null` (default): the round computed tip.
201
+ */
202
+ stamp: string | null;
203
+ /** Dab engine: spray marks. Each dab lays a seeded scatter of droplets across the tip; `null` (default): one mark per dab. */
204
+ spray: SpraySettings | null;
205
+ /**
206
+ * The brush's own gradient paint (`@pixerate/editor/paint` `PaintGradientSource`), used instead
207
+ * of the editor's paint; `null` (default): the editor's paint. Stops may be `paint` / `secondary`
208
+ * (the editor's two colours). This is what the multi-colour brush became.
209
+ */
210
+ paint: PaintGradientSource | null;
211
+ /** True for the built-in presets (they cannot be removed or overwritten). */
212
+ builtin?: boolean;
213
+ }
214
+ export declare function normalizeDynamics(raw?: Partial<BrushDynamics>, base?: BrushDynamics): BrushDynamics;
215
+ export declare const BRUSH_ID: RegExp;
216
+ export declare function normalizeMultiColor(raw?: Partial<MultiColorSettings>, base?: MultiColorSettings): MultiColorSettings;
217
+ /** Behaviour input: any subset, with a partial multi-colour block. */
218
+ export type BrushBehaviorInput = Partial<Omit<BrushBehavior, 'multicolor'>> & {
219
+ multicolor?: Partial<MultiColorSettings>;
220
+ };
221
+ export declare function normalizeBehavior(raw?: BrushBehaviorInput, base?: BrushBehavior): BrushBehavior;
222
+ /** Brush settings to change or read: any subset of a brush and of its blocks. */
223
+ /** Spray marks (dab engine). */
224
+ export interface SpraySettings {
225
+ /** Droplets per dab: 1 + density × 31 (0..1, default 0.5). */
226
+ density: number;
227
+ /** Droplet size as a fraction of the tip size, 0.02..1 (default 0.12). */
228
+ size: number;
229
+ }
230
+ export declare const DEFAULT_SPRAY: Readonly<SpraySettings>;
231
+ export type PaintBrushInput = Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer' | 'behavior' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker' | 'paint' | 'light' | 'spray'>> & {
232
+ spray?: Partial<SpraySettings> | null;
233
+ light?: Partial<LightSettings>;
234
+ /** A gradient (or `null` for the editor's paint); a plain colour source clears it. */
235
+ paint?: PaintSource | null;
236
+ tip?: Partial<BrushSettings>;
237
+ dynamics?: Partial<BrushDynamics>;
238
+ mixer?: Partial<MixerSettings>;
239
+ behavior?: BrushBehaviorInput;
240
+ blender?: Partial<BlenderSettings>;
241
+ smudge?: Partial<SmudgeSettings>;
242
+ wet?: Partial<WetBrushSettings>;
243
+ impasto?: Partial<ImpastoSettings>;
244
+ particle?: Partial<ParticleSettings>;
245
+ bristle?: Partial<BristleSettings>;
246
+ sticker?: Partial<StickerSettings>;
247
+ };
248
+ /** Whether a brush can pick paint up from the canvas (the pick-up / mix ratios apply): dab, mixer and wet brushes. */
249
+ export declare function isWetCapable(brush: Pick<PaintBrush, 'engine'>): boolean;
250
+ /** Whether a brush paints wet media (on the layer's wet layer). */
251
+ export declare function isWetBrush(brush: Pick<PaintBrush, 'engine'>): boolean;
252
+ /**
253
+ * Whether a dab brush paints dab by dab through the exchange (it picks up paint or paints a
254
+ * gradient) instead of building one coverage buffer of one colour.
255
+ */
256
+ export declare function usesPaintExchange(brush: PaintBrush, gradient?: boolean): boolean;
257
+ /** Whether strokes of a brush paint colour, so gradient paint applies (not erasers, blenders, smudges or the wet tools other than paint). */
258
+ export declare function brushTakesPaint(brush: Pick<PaintBrush, 'engine' | 'loadsColor' | 'wet'>): boolean;
259
+ /**
260
+ * The multi-colour settings a gradient paints the same as (the inverse of `multiColorToPaint`), or
261
+ * `null` when the gradient has no multi-colour equivalent. Brushes report it as
262
+ * `behavior.multicolor`, so older readers keep working.
263
+ */
264
+ export declare function paintToMultiColor(g: PaintGradientSource | null): MultiColorSettings | null;
265
+ /**
266
+ * The gradient paint a multi-coloured brush (`behavior.multicolor`, before gradient paint) paints
267
+ * the same pixels with: `tip` → `across-tip`; `dab` in sequence → `distance` in dabs, repeating
268
+ * through the colours and back to the first; `dab` at random → `random` per run of dabs, with hard
269
+ * stops. Without 2 colours of its own it uses the editor's paint and second colours.
270
+ */
271
+ export declare function multiColorToPaint(mc: MultiColorSettings, mixing: MixingModel): PaintGradientSource | null;
272
+ /** A brush with every field in range; missing fields come from `base` (default: a plain round dab brush). */
273
+ export declare function normalizePaintBrush(raw?: PaintBrushInput, base?: PaintBrush): PaintBrush;
@@ -0,0 +1,273 @@
1
+ /**
2
+ * The flat engine brush (`PaintBrush`) the paint engine reads, its settings and
3
+ * `normalizePaintBrush`. Kept apart from the built-in library (`./brushes`) so the facet
4
+ * resolver (`brushes/resolve`) can import it while `BUILTIN_BRUSHES` resolves the facet presets.
5
+ */
6
+ import { type BrushSettings } from '../paint/brush.js';
7
+ import { type BlenderSettings, type SmudgeSettings } from '../paint/blending.js';
8
+ import type { MixingModel } from '../paint/exchange.js';
9
+ import { type PaintGradientSource, type PaintSource } from '../paint/gradient-paint.js';
10
+ import { type MixerSettings } from '../paint/mixer.js';
11
+ import { type BristleSettings, type ParticleSettings, type StickerSettings } from './mark-settings.js';
12
+ /**
13
+ * How a brush makes marks: `dab` (dabs into a stroke buffer, or dab by dab through the pick-up /
14
+ * mix exchange when its behaviour asks for it), `eraser` (removes alpha), `mixer` (the Mixer
15
+ * Brush), `blender` (mixes the colours under and near the tip) or `smudge` (drags paint along).
16
+ */
17
+ export type PaintBrushEngine = 'dab' | 'eraser' | 'mixer' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker';
18
+ export declare const PAINT_BRUSH_ENGINES: readonly PaintBrushEngine[];
19
+ export type PaintBrushCategory = 'pencil' | 'pastel' | 'charcoal' | 'airbrush' | 'marker' | 'ink' | 'paint' | 'eraser' | 'blender' | 'smudge' | 'mixer' | 'watercolor' | 'wet-tool' | 'oil' | 'impasto' | 'procedural' | 'bristle' | 'sticker' | 'light' | 'custom';
20
+ export declare const PAINT_BRUSH_CATEGORIES: readonly PaintBrushCategory[];
21
+ /**
22
+ * What a wet brush does to the wet layer: `paint` (water and pigment), `water` (clear water;
23
+ * with `rewet` it lifts dried paint), `dry` (a hair dryer: takes water, the pigment settles),
24
+ * `blow` (pushes the water along the stroke), `blend` (evens out the wet pigment under the tip),
25
+ * `smudge` (drags wet paint along) or `blot` (a tissue: takes up wet paint).
26
+ */
27
+ export type WetTool = 'paint' | 'water' | 'dry' | 'blow' | 'blend' | 'smudge' | 'blot';
28
+ export declare const WET_TOOLS: readonly WetTool[];
29
+ /**
30
+ * The wet medium a wet brush paints: its layer takes the medium's wet settings the first time it
31
+ * holds wet paint (`watercolor`: runny, dries in seconds, granulates and darkens at edges;
32
+ * `gouache`: opaque, flatter; `oil`: thick, barely flows, stays wet for a long time and blends into
33
+ * the wet paint it meets).
34
+ */
35
+ export type WetMediumKind = 'watercolor' | 'gouache' | 'oil';
36
+ export declare const WET_MEDIA: readonly WetMediumKind[];
37
+ /** Wet engine settings, each 0..1. */
38
+ export interface WetBrushSettings {
39
+ tool: WetTool;
40
+ /** Water the brush lays down (wetness): 0.2 a damp brush, 1 a dripping one. */
41
+ water: number;
42
+ /** Pigment concentration: 0 clear water .. 1 paint straight from the tube. */
43
+ pigment: number;
44
+ /** Body: 0 a transparent watercolour .. 1 opaque gouache that hides what is under it. */
45
+ body: number;
46
+ /** Strength of the dry, blow, blend, smudge and blot tools. */
47
+ strength: number;
48
+ /** How much dried paint the water lifts again (re-wetting): 0 none (staining) .. 1. */
49
+ rewet: number;
50
+ /** The medium (its layer's wet settings the first time it holds wet paint). */
51
+ medium: WetMediumKind;
52
+ /** Paint loading: 1 never runs out; lower, the brush runs dry along a long stroke (reload each stroke). */
53
+ load: number;
54
+ /** Thinner (water for watercolour, solvent for oil): thins the paint, so it is more transparent and runnier. */
55
+ thinner: number;
56
+ }
57
+ export declare const DEFAULT_WET_BRUSH: Readonly<WetBrushSettings>;
58
+ export declare function normalizeWetBrush(raw?: Partial<WetBrushSettings>, base?: WetBrushSettings): WetBrushSettings;
59
+ /**
60
+ * What an impasto brush does on a thick-paint layer: `brush` (paint with body: colour and relief),
61
+ * `tube` (squeezes blobs of paint straight from the tube), `knife` (a palette knife: scrapes the
62
+ * relief flat and smears the paint along), `roller` (a flat, even band with a stipple), `gloop`
63
+ * (a thick, glossy, rounded line).
64
+ */
65
+ export type ImpastoTool = 'brush' | 'tube' | 'knife' | 'roller' | 'gloop';
66
+ export declare const IMPASTO_TOOLS: readonly ImpastoTool[];
67
+ /** Impasto engine settings, each 0..1. */
68
+ export interface ImpastoSettings {
69
+ tool: ImpastoTool;
70
+ /** How thick the paint stands up (relief per stroke). */
71
+ thickness: number;
72
+ /** Shine of the paint when lit (0 matte .. 1 wet-looking gloss). */
73
+ gloss: number;
74
+ /** Metallic paint: highlights take the paint's colour (gold, copper, silver). */
75
+ metallic: number;
76
+ /** Paint loading: 1 never runs out; lower, the stroke thins and fades as the brush empties. */
77
+ load: number;
78
+ /** Thinner: flatter, more transparent paint that levels out. */
79
+ thinner: number;
80
+ /** Knife and brush: how much of the paint under the tool is dragged along (0..1). */
81
+ drag: number;
82
+ /** Light paint: the bead's light is stronger where its surface turns away, as on backlit glass (0..1, default 0). */
83
+ rim: number;
84
+ /** Light paint: the middle of a bead lets the layers below show through, its edges stay solid (0..1, default 0). */
85
+ clarity: number;
86
+ }
87
+ export declare const DEFAULT_IMPASTO: Readonly<ImpastoSettings>;
88
+ /** Light paint: how much light a brush lays with its colour (see `doc.light`). */
89
+ export interface LightSettings {
90
+ /** Light each dab lays, scaled by its coverage and flow (0..1, default 0: none). */
91
+ amount: number;
92
+ /** Pressure scales the light (default false). */
93
+ pressure: boolean;
94
+ }
95
+ export declare const DEFAULT_LIGHT: Readonly<LightSettings>;
96
+ export declare function normalizeLight(raw?: Partial<LightSettings>, base?: LightSettings): LightSettings;
97
+ export declare function normalizeImpasto(raw?: Partial<ImpastoSettings>, base?: ImpastoSettings): ImpastoSettings;
98
+ /** Multi-coloured brush: `tip` lays the colours across the tip, `dab` changes colour from dab to dab. */
99
+ export type MultiColorMode = 'off' | 'tip' | 'dab';
100
+ export declare const MULTI_COLOR_MODES: readonly MultiColorMode[];
101
+ export interface MultiColorSettings {
102
+ /** `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). */
103
+ mode: MultiColorMode;
104
+ /** 2..8 colours #rrggbb. Fewer than 2: the paint colour and the secondary colour. */
105
+ colors: string[];
106
+ /** `dab` mode: `sequence` (default) runs through the colours in order, blending between them; `random` picks one per run of dabs (seeded, so replays match). */
107
+ order: 'sequence' | 'random';
108
+ /** `dab` mode: dabs per colour, 1..500 (default 12). */
109
+ length: number;
110
+ /** How softly neighbouring colours blend, 0 (hard stripes or steps) .. 1 (smooth gradient, default). */
111
+ softness: number;
112
+ }
113
+ export declare const DEFAULT_MULTI_COLOR: Readonly<MultiColorSettings>;
114
+ /**
115
+ * How a brush handles paint on the canvas. The pick-up / mix ratios are the shared exchange's
116
+ * (`@pixerate/editor/paint` `PaintExchange`); wet media will use the same ones. On a Mixer Brush
117
+ * they are its `mixer.wet`, `mixer.mix`, `!mixer.cleanEachStroke` and `mixer.mixing`, kept in sync.
118
+ */
119
+ export interface BrushBehavior {
120
+ /** Pick-up ratio, 0..1: how much of the paint under each dab the brush lifts and carries (0: none, default). */
121
+ pickup: number;
122
+ /** Mix ratio, 0..1: how much of the carried paint goes into what the brush lays down, against its own colour (default 0.5). */
123
+ mix: number;
124
+ /** Dirty brush: keep the carried paint after the stroke, so the next stroke starts with the last colour the brush touched (default false). */
125
+ dirty: boolean;
126
+ /** How picked-up paint, multi-colour gradients and the deposit mix: `pigment` (default, Kubelka-Munk) or `rgb`. */
127
+ mixing: MixingModel;
128
+ /** Multi-coloured brush. */
129
+ multicolor: MultiColorSettings;
130
+ }
131
+ export declare const DEFAULT_BEHAVIOR: Readonly<BrushBehavior>;
132
+ /** What turns the tip: a fixed angle, the direction of the stroke, or the pen's barrel rotation. */
133
+ export type BrushRotation = 'fixed' | 'stroke' | 'barrel';
134
+ export declare const BRUSH_ROTATIONS: readonly BrushRotation[];
135
+ /** Pen dynamics and jitter. Every amount is 0 (off) .. 1 unless noted. */
136
+ export interface BrushDynamics {
137
+ /** Size at zero pressure as a fraction of the full size, when pressure controls size (default 0). */
138
+ minSize: number;
139
+ /** How much pressure scales flow (default 0). */
140
+ pressureFlow: number;
141
+ /** Laying the pen down widens the tip, up to 2.5× (default 0). */
142
+ tiltSize: number;
143
+ /** Laying the pen down flattens the tip along the tilt direction (default 0). */
144
+ tiltRoundness: number;
145
+ /** Laying the pen down lightens the mark, as when shading with the side of a pencil (default 0). */
146
+ tiltOpacity: number;
147
+ /** -1..1: positive grows the tip when the pen moves fast, negative thins it (default 0). */
148
+ velocitySize: number;
149
+ /** -1..1: positive darkens fast marks, negative lightens them (default 0). */
150
+ velocityOpacity: number;
151
+ /** Random size change per dab (default 0). */
152
+ sizeJitter: number;
153
+ /** Random angle per dab, as a fraction of ±180° (default 0). */
154
+ angleJitter: number;
155
+ /** Random opacity drop per dab (default 0). */
156
+ opacityJitter: number;
157
+ /** Random offset across the stroke in diameters, 0..4 (default 0). */
158
+ scatter: number;
159
+ }
160
+ export declare const DEFAULT_DYNAMICS: Readonly<BrushDynamics>;
161
+ export interface PaintBrush {
162
+ /** Library id (lowercase letters, digits and dashes). */
163
+ id: string;
164
+ name: string;
165
+ category: PaintBrushCategory;
166
+ engine: PaintBrushEngine;
167
+ /** The shared engine's tip: size, hardness, spacing, angle, roundness, opacity, flow, position smoothing, pressure to size / opacity, blend. */
168
+ tip: BrushSettings;
169
+ /** How strongly the mark follows the paper grain, 0 (ignores it) .. 1 (catches only the peaks at light pressure). */
170
+ grain: number;
171
+ /** Pressure smoothing 0..0.95, separate from the tip's position smoothing (default 0). */
172
+ pressureSmoothing: number;
173
+ rotation: BrushRotation;
174
+ dynamics: BrushDynamics;
175
+ /** Mixer engine only: the Mixer Brush settings. */
176
+ mixer: MixerSettings;
177
+ /** Mixer engine only: load the current colour each stroke (false: a smudge or blender that only moves paint). */
178
+ loadsColor: boolean;
179
+ /** Pick-up and mix ratios, dirty brush and multi-colour (dab and mixer engines). */
180
+ behavior: BrushBehavior;
181
+ /** Blender engine only: strength, blend distance and mixing. */
182
+ blender: BlenderSettings;
183
+ /** Smudge engine only: strength, length and mixing. */
184
+ smudge: SmudgeSettings;
185
+ /** Wet engine only: the wet tool, water, pigment, body and strength. */
186
+ wet: WetBrushSettings;
187
+ /** Impasto engine only: the tool, thickness, gloss, metallic, loading, thinner and drag. */
188
+ impasto: ImpastoSettings;
189
+ /** Particle engine only: the particles' kind, how they are drawn, and their physics. */
190
+ particle: ParticleSettings;
191
+ /** Bristle engine only: the tuft. */
192
+ bristle: BristleSettings;
193
+ /** Sticker engine only: the stamps and how they are sprayed. */
194
+ sticker: StickerSettings;
195
+ /** Light paint: the light it lays with its colour (amount 0: none). */
196
+ light: LightSettings;
197
+ /**
198
+ * Dab engine: a stamp tip. Each dab (and each spray droplet) is this sticker's shape, an editor
199
+ * stamp (`editor:star`) or any sticker ref from the sticker library, laid in the paint colour;
200
+ * `null` (default): the round computed tip.
201
+ */
202
+ stamp: string | null;
203
+ /** Dab engine: spray marks. Each dab lays a seeded scatter of droplets across the tip; `null` (default): one mark per dab. */
204
+ spray: SpraySettings | null;
205
+ /**
206
+ * The brush's own gradient paint (`@pixerate/editor/paint` `PaintGradientSource`), used instead
207
+ * of the editor's paint; `null` (default): the editor's paint. Stops may be `paint` / `secondary`
208
+ * (the editor's two colours). This is what the multi-colour brush became.
209
+ */
210
+ paint: PaintGradientSource | null;
211
+ /** True for the built-in presets (they cannot be removed or overwritten). */
212
+ builtin?: boolean;
213
+ }
214
+ export declare function normalizeDynamics(raw?: Partial<BrushDynamics>, base?: BrushDynamics): BrushDynamics;
215
+ export declare const BRUSH_ID: RegExp;
216
+ export declare function normalizeMultiColor(raw?: Partial<MultiColorSettings>, base?: MultiColorSettings): MultiColorSettings;
217
+ /** Behaviour input: any subset, with a partial multi-colour block. */
218
+ export type BrushBehaviorInput = Partial<Omit<BrushBehavior, 'multicolor'>> & {
219
+ multicolor?: Partial<MultiColorSettings>;
220
+ };
221
+ export declare function normalizeBehavior(raw?: BrushBehaviorInput, base?: BrushBehavior): BrushBehavior;
222
+ /** Brush settings to change or read: any subset of a brush and of its blocks. */
223
+ /** Spray marks (dab engine). */
224
+ export interface SpraySettings {
225
+ /** Droplets per dab: 1 + density × 31 (0..1, default 0.5). */
226
+ density: number;
227
+ /** Droplet size as a fraction of the tip size, 0.02..1 (default 0.12). */
228
+ size: number;
229
+ }
230
+ export declare const DEFAULT_SPRAY: Readonly<SpraySettings>;
231
+ export type PaintBrushInput = Partial<Omit<PaintBrush, 'tip' | 'dynamics' | 'mixer' | 'behavior' | 'blender' | 'smudge' | 'wet' | 'impasto' | 'particle' | 'bristle' | 'sticker' | 'paint' | 'light' | 'spray'>> & {
232
+ spray?: Partial<SpraySettings> | null;
233
+ light?: Partial<LightSettings>;
234
+ /** A gradient (or `null` for the editor's paint); a plain colour source clears it. */
235
+ paint?: PaintSource | null;
236
+ tip?: Partial<BrushSettings>;
237
+ dynamics?: Partial<BrushDynamics>;
238
+ mixer?: Partial<MixerSettings>;
239
+ behavior?: BrushBehaviorInput;
240
+ blender?: Partial<BlenderSettings>;
241
+ smudge?: Partial<SmudgeSettings>;
242
+ wet?: Partial<WetBrushSettings>;
243
+ impasto?: Partial<ImpastoSettings>;
244
+ particle?: Partial<ParticleSettings>;
245
+ bristle?: Partial<BristleSettings>;
246
+ sticker?: Partial<StickerSettings>;
247
+ };
248
+ /** Whether a brush can pick paint up from the canvas (the pick-up / mix ratios apply): dab, mixer and wet brushes. */
249
+ export declare function isWetCapable(brush: Pick<PaintBrush, 'engine'>): boolean;
250
+ /** Whether a brush paints wet media (on the layer's wet layer). */
251
+ export declare function isWetBrush(brush: Pick<PaintBrush, 'engine'>): boolean;
252
+ /**
253
+ * Whether a dab brush paints dab by dab through the exchange (it picks up paint or paints a
254
+ * gradient) instead of building one coverage buffer of one colour.
255
+ */
256
+ export declare function usesPaintExchange(brush: PaintBrush, gradient?: boolean): boolean;
257
+ /** Whether strokes of a brush paint colour, so gradient paint applies (not erasers, blenders, smudges or the wet tools other than paint). */
258
+ export declare function brushTakesPaint(brush: Pick<PaintBrush, 'engine' | 'loadsColor' | 'wet'>): boolean;
259
+ /**
260
+ * The multi-colour settings a gradient paints the same as (the inverse of `multiColorToPaint`), or
261
+ * `null` when the gradient has no multi-colour equivalent. Brushes report it as
262
+ * `behavior.multicolor`, so older readers keep working.
263
+ */
264
+ export declare function paintToMultiColor(g: PaintGradientSource | null): MultiColorSettings | null;
265
+ /**
266
+ * The gradient paint a multi-coloured brush (`behavior.multicolor`, before gradient paint) paints
267
+ * the same pixels with: `tip` → `across-tip`; `dab` in sequence → `distance` in dabs, repeating
268
+ * through the colours and back to the first; `dab` at random → `random` per run of dabs, with hard
269
+ * stops. Without 2 colours of its own it uses the editor's paint and second colours.
270
+ */
271
+ export declare function multiColorToPaint(mc: MultiColorSettings, mixing: MixingModel): PaintGradientSource | null;
272
+ /** A brush with every field in range; missing fields come from `base` (default: a plain round dab brush). */
273
+ export declare function normalizePaintBrush(raw?: PaintBrushInput, base?: PaintBrush): PaintBrush;