@pixerate/editor 0.274.0 → 0.275.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 (86) hide show
  1. package/dist/animation-editor/index.cjs +60 -60
  2. package/dist/animation-editor/index.js +4 -4
  3. package/dist/brushes/index.cjs +102 -74
  4. package/dist/brushes/index.js +34 -6
  5. package/dist/brushes/patch.d.cts +1 -1
  6. package/dist/brushes/patch.d.ts +1 -1
  7. package/dist/brushes/preview.d.cts +3 -0
  8. package/dist/brushes/preview.d.ts +3 -0
  9. package/dist/brushes/profiles.d.cts +2 -0
  10. package/dist/brushes/profiles.d.ts +2 -0
  11. package/dist/brushes/schema.d.cts +4 -0
  12. package/dist/brushes/schema.d.ts +4 -0
  13. package/dist/brushes/types.d.cts +47 -0
  14. package/dist/brushes/types.d.ts +47 -0
  15. package/dist/{brushstrokeRender-5YBCLFDQ.cjs → brushstrokeRender-2RQL46WB.cjs} +15 -15
  16. package/dist/{brushstrokeRender-DZGFU3J4.js → brushstrokeRender-WGY4DZHF.js} +3 -3
  17. package/dist/canvas/index.cjs +13 -13
  18. package/dist/canvas/index.js +1 -1
  19. package/dist/chart/contract.d.cts +3 -1
  20. package/dist/chart/contract.d.ts +3 -1
  21. package/dist/{chunk-2XDNF73E.js → chunk-43XJC3PP.js} +1 -1
  22. package/dist/{chunk-GHGBIAWQ.cjs → chunk-6QNDGMK7.cjs} +263 -45
  23. package/dist/{chunk-2F3YDXCN.js → chunk-75SYKUOT.js} +1 -1
  24. package/dist/{chunk-M3RZOF3G.cjs → chunk-C6VB7CPY.cjs} +1 -1
  25. package/dist/{chunk-N5IMDAHM.cjs → chunk-DHC4E2DU.cjs} +89 -37
  26. package/dist/{chunk-NAHJV3O6.js → chunk-G5TCMKX5.js} +28 -11
  27. package/dist/{chunk-DZVRH63Y.js → chunk-IEEY5RJV.js} +1 -1
  28. package/dist/{chunk-7W2EQQBP.cjs → chunk-JDXWLCXF.cjs} +6 -6
  29. package/dist/{chunk-N3SKPQEH.js → chunk-LMG57L5A.js} +62 -10
  30. package/dist/{chunk-5HP2VSAS.js → chunk-MSWLJI4G.js} +1 -1
  31. package/dist/{chunk-SNAFFCAX.cjs → chunk-OLZXXAER.cjs} +221 -196
  32. package/dist/{chunk-YJANF2BL.cjs → chunk-RAPNRK4R.cjs} +86 -86
  33. package/dist/{chunk-CLB2I2FU.js → chunk-RJGW3RQS.js} +2 -2
  34. package/dist/{chunk-FXVCB6YG.cjs → chunk-T6KNJVBP.cjs} +35 -35
  35. package/dist/{chunk-ZEI7ZEA4.js → chunk-Y2ISSVPH.js} +34 -9
  36. package/dist/{chunk-3JKCREEU.cjs → chunk-Y354ETAE.cjs} +76 -59
  37. package/dist/{chunk-TNLKFW7D.cjs → chunk-YYZC2EQO.cjs} +23 -23
  38. package/dist/{chunk-UXYCY3HP.js → chunk-ZM7IKQ4X.js} +239 -21
  39. package/dist/image-editor/index.cjs +789 -789
  40. package/dist/image-editor/index.js +5 -5
  41. package/dist/paint/index.cjs +2 -2
  42. package/dist/paint/index.js +1 -1
  43. package/dist/paint/wet/world.d.cts +15 -2
  44. package/dist/paint/wet/world.d.ts +15 -2
  45. package/dist/paint-editor/brush-controls.d.cts +7 -0
  46. package/dist/paint-editor/brush-controls.d.ts +7 -0
  47. package/dist/paint-editor/brush-model.d.cts +59 -1
  48. package/dist/paint-editor/brush-model.d.ts +59 -1
  49. package/dist/paint-editor/controller.d.cts +168 -1
  50. package/dist/paint-editor/controller.d.ts +168 -1
  51. package/dist/paint-editor/dab-stroke.d.cts +3 -0
  52. package/dist/paint-editor/dab-stroke.d.ts +3 -0
  53. package/dist/paint-editor/document.d.cts +13 -3
  54. package/dist/paint-editor/document.d.ts +13 -3
  55. package/dist/paint-editor/engine.d.cts +3 -0
  56. package/dist/paint-editor/engine.d.ts +3 -0
  57. package/dist/paint-editor/heat.d.cts +156 -0
  58. package/dist/paint-editor/heat.d.ts +156 -0
  59. package/dist/paint-editor/index.cjs +3386 -855
  60. package/dist/paint-editor/index.d.cts +3 -0
  61. package/dist/paint-editor/index.d.ts +3 -0
  62. package/dist/paint-editor/index.js +2731 -200
  63. package/dist/paint-editor/mutation-tools.d.cts +13 -0
  64. package/dist/paint-editor/mutation-tools.d.ts +13 -0
  65. package/dist/paint-editor/mutation.d.cts +67 -0
  66. package/dist/paint-editor/mutation.d.ts +67 -0
  67. package/dist/paint-editor/presence.d.cts +7 -1
  68. package/dist/paint-editor/presence.d.ts +7 -1
  69. package/dist/paint-editor/procedural.d.cts +3 -0
  70. package/dist/paint-editor/procedural.d.ts +3 -0
  71. package/dist/paint-editor/stroke.d.cts +32 -2
  72. package/dist/paint-editor/stroke.d.ts +32 -2
  73. package/dist/paint-editor/thick.d.cts +48 -7
  74. package/dist/paint-editor/thick.d.ts +48 -7
  75. package/dist/paint-editor/ui.d.cts +34 -1
  76. package/dist/paint-editor/ui.d.ts +34 -1
  77. package/dist/paint-editor/wet.d.cts +8 -1
  78. package/dist/paint-editor/wet.d.ts +8 -1
  79. package/dist/vector/index.cjs +999 -999
  80. package/dist/vector/index.js +5 -5
  81. package/package.json +1 -1
  82. package/skills/pixerate-brushes/SKILL.md +1 -0
  83. package/skills/pixerate-paint-editor/SKILL.md +7 -1
  84. package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
  85. package/skills/pixerate-paint-editor/references/live-patterns.md +41 -0
  86. package/skills/pixerate-paint-watercolour/SKILL.md +8 -0
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Agent tools for mutation paint (`createMutationTools`, included in `createPaintEditorToolProvider`):
3
+ * `list_mutations`, `read_mutation`, `answer_mutation`, and `decline_mutation`.
4
+ */
5
+ import type { PresenceActorInfo } from '../animation/presence.cjs';
6
+ import type { PaintEditorController } from './controller.cjs';
7
+ import type { PaintAgentTool } from './hosted.cjs';
8
+ import type { PaintDirector } from './presence.cjs';
9
+ export interface MutationToolsOptions {
10
+ director?: PaintDirector;
11
+ actor?: PresenceActorInfo;
12
+ }
13
+ export declare function createMutationTools(c: PaintEditorController, options?: MutationToolsOptions): PaintAgentTool[];
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Agent tools for mutation paint (`createMutationTools`, included in `createPaintEditorToolProvider`):
3
+ * `list_mutations`, `read_mutation`, `answer_mutation`, and `decline_mutation`.
4
+ */
5
+ import type { PresenceActorInfo } from '../animation/presence.js';
6
+ import type { PaintEditorController } from './controller.js';
7
+ import type { PaintAgentTool } from './hosted.js';
8
+ import type { PaintDirector } from './presence.js';
9
+ export interface MutationToolsOptions {
10
+ director?: PaintDirector;
11
+ actor?: PresenceActorInfo;
12
+ }
13
+ export declare function createMutationTools(c: PaintEditorController, options?: MutationToolsOptions): PaintAgentTool[];
@@ -0,0 +1,67 @@
1
+ import type { RasterRect } from '../raster/surface.cjs';
2
+ import type { PaintBrush } from './brush-model.cjs';
3
+ import type { PaintStrokeInit } from './controller.cjs';
4
+ import type { PenPoint } from './engine.cjs';
5
+ export type PaintMutationState = 'settling' | 'pending' | 'claimed' | 'answered' | 'declined';
6
+ export type PaintMutationMedium = 'wet' | 'dry';
7
+ export interface PaintMutationStroke {
8
+ id: string;
9
+ color: string;
10
+ brushId: string;
11
+ brushName: string;
12
+ size: number;
13
+ points: PenPoint[];
14
+ bounds: RasterRect | null;
15
+ }
16
+ export interface PaintMutationInfo {
17
+ id: string;
18
+ state: PaintMutationState;
19
+ layerId: string;
20
+ medium: PaintMutationMedium;
21
+ bounds: RasterRect | null;
22
+ strokeCount: number;
23
+ age: number;
24
+ settle: number;
25
+ patience: number;
26
+ fade: number;
27
+ claimedBy?: string;
28
+ answerStrokeIds?: string[];
29
+ declineReason?: string;
30
+ }
31
+ export interface PaintMutationPng {
32
+ width: number;
33
+ height: number;
34
+ data: string;
35
+ }
36
+ export interface PaintMutationCropPng extends PaintMutationPng {
37
+ rect: RasterRect;
38
+ }
39
+ export interface PaintMutationReadResult {
40
+ id: string;
41
+ state: 'claimed';
42
+ layerId: string;
43
+ medium: PaintMutationMedium;
44
+ bounds: RasterRect | null;
45
+ fade: number;
46
+ strokes: PaintMutationStroke[];
47
+ cropPng: PaintMutationCropPng;
48
+ canvasPng: PaintMutationPng;
49
+ }
50
+ export interface PaintMutationAnswerInput {
51
+ strokes: ReadonlyArray<PaintStrokeInit & {
52
+ points: readonly PenPoint[];
53
+ }>;
54
+ layerId?: string;
55
+ note?: string;
56
+ }
57
+ export interface PaintMutationAnswerResult {
58
+ id: string;
59
+ state: 'answered';
60
+ layerId: string;
61
+ strokeIds: string[];
62
+ bounds: RasterRect | null;
63
+ dabs: number;
64
+ wet?: boolean;
65
+ }
66
+ export type PaintMutationRequestListener = (mutation: PaintMutationInfo) => void;
67
+ export declare function mutationBrushRefusal(brush: PaintBrush): string | null;
@@ -0,0 +1,67 @@
1
+ import type { RasterRect } from '../raster/surface.js';
2
+ import type { PaintBrush } from './brush-model.js';
3
+ import type { PaintStrokeInit } from './controller.js';
4
+ import type { PenPoint } from './engine.js';
5
+ export type PaintMutationState = 'settling' | 'pending' | 'claimed' | 'answered' | 'declined';
6
+ export type PaintMutationMedium = 'wet' | 'dry';
7
+ export interface PaintMutationStroke {
8
+ id: string;
9
+ color: string;
10
+ brushId: string;
11
+ brushName: string;
12
+ size: number;
13
+ points: PenPoint[];
14
+ bounds: RasterRect | null;
15
+ }
16
+ export interface PaintMutationInfo {
17
+ id: string;
18
+ state: PaintMutationState;
19
+ layerId: string;
20
+ medium: PaintMutationMedium;
21
+ bounds: RasterRect | null;
22
+ strokeCount: number;
23
+ age: number;
24
+ settle: number;
25
+ patience: number;
26
+ fade: number;
27
+ claimedBy?: string;
28
+ answerStrokeIds?: string[];
29
+ declineReason?: string;
30
+ }
31
+ export interface PaintMutationPng {
32
+ width: number;
33
+ height: number;
34
+ data: string;
35
+ }
36
+ export interface PaintMutationCropPng extends PaintMutationPng {
37
+ rect: RasterRect;
38
+ }
39
+ export interface PaintMutationReadResult {
40
+ id: string;
41
+ state: 'claimed';
42
+ layerId: string;
43
+ medium: PaintMutationMedium;
44
+ bounds: RasterRect | null;
45
+ fade: number;
46
+ strokes: PaintMutationStroke[];
47
+ cropPng: PaintMutationCropPng;
48
+ canvasPng: PaintMutationPng;
49
+ }
50
+ export interface PaintMutationAnswerInput {
51
+ strokes: ReadonlyArray<PaintStrokeInit & {
52
+ points: readonly PenPoint[];
53
+ }>;
54
+ layerId?: string;
55
+ note?: string;
56
+ }
57
+ export interface PaintMutationAnswerResult {
58
+ id: string;
59
+ state: 'answered';
60
+ layerId: string;
61
+ strokeIds: string[];
62
+ bounds: RasterRect | null;
63
+ dabs: number;
64
+ wet?: boolean;
65
+ }
66
+ export type PaintMutationRequestListener = (mutation: PaintMutationInfo) => void;
67
+ export declare function mutationBrushRefusal(brush: PaintBrush): string | null;
@@ -95,9 +95,15 @@ export declare class PaintDirector {
95
95
  private readonly options;
96
96
  private readonly clock;
97
97
  private readonly queue;
98
+ private readonly cancelled;
99
+ private currentRun;
98
100
  constructor(controller: PaintEditorController, presence: PaintPresence, options?: PaintDirectorOptions);
101
+ /** Cancels any queued or running performance with `key` (e.g. a mutation that was undone). */
102
+ cancel(key: string): void;
99
103
  /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
100
- perform(actor: PresenceActorInfo, commands: readonly PaintAgentCommand[]): Promise<PaintPerformanceResult[]>;
104
+ perform(actor: PresenceActorInfo, commands: readonly PaintAgentCommand[], options?: {
105
+ key?: string;
106
+ }): Promise<PaintPerformanceResult[]>;
101
107
  private ms;
102
108
  private where;
103
109
  /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
@@ -95,9 +95,15 @@ export declare class PaintDirector {
95
95
  private readonly options;
96
96
  private readonly clock;
97
97
  private readonly queue;
98
+ private readonly cancelled;
99
+ private currentRun;
98
100
  constructor(controller: PaintEditorController, presence: PaintPresence, options?: PaintDirectorOptions);
101
+ /** Cancels any queued or running performance with `key` (e.g. a mutation that was undone). */
102
+ cancel(key: string): void;
99
103
  /** Acts out `commands` in order for `actor`. Resolves with one result per command. */
100
- perform(actor: PresenceActorInfo, commands: readonly PaintAgentCommand[]): Promise<PaintPerformanceResult[]>;
104
+ perform(actor: PresenceActorInfo, commands: readonly PaintAgentCommand[], options?: {
105
+ key?: string;
106
+ }): Promise<PaintPerformanceResult[]>;
101
107
  private ms;
102
108
  private where;
103
109
  /** Glides to `to`; an actor that just appeared starts there instead of the corner. */
@@ -29,6 +29,7 @@ import { type EffectPaintPixels } from './effect-paint.cjs';
29
29
  import { PenDabWalker, PenSmoother, type PenDab, type PenPoint, type StrokeCopy } from './engine.cjs';
30
30
  import { type PaperGrain } from './paper.cjs';
31
31
  import type { StickerArtLookup } from './sticker-art.cjs';
32
+ import type { FilmBuffer } from './thick.cjs';
32
33
  export { DEFAULT_BRISTLE, DEFAULT_PARTICLE, DOT_ROTATIONS, DOT_TEXTURE_ORDERS, MAX_DOT_TEXTURES, normalizeBristle, normalizeParticle, PARTICLE_KINDS, PARTICLE_RENDERS, RIBBON_PAIRS, type BristleSettings, type DotRotation, type DotTextureOrder, type ParticleKind, type ParticleRender, type ParticleSettings, type RibbonPairs, } from './mark-settings.cjs';
33
34
  export interface ProceduralStrokeOptions {
34
35
  grain: PaperGrain | null;
@@ -41,6 +42,8 @@ export interface ProceduralStrokeOptions {
41
42
  effect?: EffectPaintPixels | null;
42
43
  /** Loaded sticker art, for textured dots of stickers from the sticker library. */
43
44
  stampArt?: StickerArtLookup;
45
+ /** Thin-film iridescence channel for procedural brushes with `iridescence`. */
46
+ film?: FilmBuffer | null;
44
47
  }
45
48
  /** What a coverage-building stroke engine provides to the stroke session. */
46
49
  export interface CoverageStroke {
@@ -29,6 +29,7 @@ import { type EffectPaintPixels } from './effect-paint.js';
29
29
  import { PenDabWalker, PenSmoother, type PenDab, type PenPoint, type StrokeCopy } from './engine.js';
30
30
  import { type PaperGrain } from './paper.js';
31
31
  import type { StickerArtLookup } from './sticker-art.js';
32
+ import type { FilmBuffer } from './thick.js';
32
33
  export { DEFAULT_BRISTLE, DEFAULT_PARTICLE, DOT_ROTATIONS, DOT_TEXTURE_ORDERS, MAX_DOT_TEXTURES, normalizeBristle, normalizeParticle, PARTICLE_KINDS, PARTICLE_RENDERS, RIBBON_PAIRS, type BristleSettings, type DotRotation, type DotTextureOrder, type ParticleKind, type ParticleRender, type ParticleSettings, type RibbonPairs, } from './mark-settings.js';
33
34
  export interface ProceduralStrokeOptions {
34
35
  grain: PaperGrain | null;
@@ -41,6 +42,8 @@ export interface ProceduralStrokeOptions {
41
42
  effect?: EffectPaintPixels | null;
42
43
  /** Loaded sticker art, for textured dots of stickers from the sticker library. */
43
44
  stampArt?: StickerArtLookup;
45
+ /** Thin-film iridescence channel for procedural brushes with `iridescence`. */
46
+ film?: FilmBuffer | null;
44
47
  }
45
48
  /** What a coverage-building stroke engine provides to the stroke session. */
46
49
  export interface CoverageStroke {
@@ -15,10 +15,10 @@ import { type PaintBrush } from './brushes.cjs';
15
15
  import type { PaintGradientSource } from '../paint/gradient-paint.cjs';
16
16
  import type { EffectPaintPixels, PaintEffectPaint } from './effect-paint.cjs';
17
17
  import { type PenPoint, type StrokeCopy } from './engine.cjs';
18
- import type { PaperGrain } from './paper.cjs';
19
18
  import type { Affine } from './strokes.cjs';
20
19
  import type { WetStrokeDriver } from './wet.cjs';
21
20
  import { type ThickChannels } from './thick.cjs';
21
+ import { type PaperGrain } from './paper.cjs';
22
22
  import { type Sticker } from './stickers.cjs';
23
23
  import type { StickerArtLookup } from './sticker-art.cjs';
24
24
  import type { StickerRef, StickerVariant } from '../stickers/types.cjs';
@@ -100,6 +100,20 @@ export interface StrokeSessionInit {
100
100
  light?: {
101
101
  surface: TiledSurface;
102
102
  } | null;
103
+ /**
104
+ * UV-fluorescent ink: the layer's fluorescence channel before the stroke. With it, a twin of the
105
+ * stroke paints the 4-channel fluorescence surface; `fluorChannel()` returns the result.
106
+ */
107
+ fluor?: {
108
+ surface: TiledSurface;
109
+ } | null;
110
+ /**
111
+ * Thermochromic ink: the layer's thermochromic channel before the stroke. With it, a twin of the
112
+ * stroke paints the 4-channel thermochromic surface; `thermoChannel()` returns the result.
113
+ */
114
+ thermo?: {
115
+ surface: TiledSurface;
116
+ } | null;
103
117
  }
104
118
  export interface PaintStrokeResult {
105
119
  layerId: string;
@@ -129,10 +143,18 @@ export declare class PaintStrokeSession {
129
143
  private wet;
130
144
  private readonly wetDriver;
131
145
  private thickDriver;
146
+ private filmBuffer;
132
147
  private stickerSpray;
133
148
  /** Light paint: the stroke's twin over the light channel, and what it left there. */
134
149
  private readonly lightTwin;
135
150
  private lightResult;
151
+ /** UV-fluorescent ink: the stroke's twin over the fluorescence channel, and what it left there. */
152
+ private readonly fluorTwin;
153
+ private fluorResult;
154
+ /** Thermochromic ink: the stroke's twin over the thermochromic channel, and what it left there. */
155
+ private readonly thermoTwin;
156
+ private thermoResult;
157
+ private readonly invisibleDaylight;
136
158
  /** A wet stroke that ended while the simulation was mid-batch: it commits when the driver catches up. */
137
159
  private wetEndPending;
138
160
  private readonly apply;
@@ -154,15 +176,23 @@ export declare class PaintStrokeSession {
154
176
  repaintEffect(pixels: EffectPaintPixels): void;
155
177
  /** The stroke's twin over the light channel: the same marks in grey at the brush's light amount (see `light.ts`). */
156
178
  private makeLightTwin;
179
+ /** The stroke's twin over the 4-channel fluorescence surface (see `heat.ts`). */
180
+ private makeFluorTwin;
181
+ /** The stroke's twin over the 4-channel thermochromic surface (see `heat.ts`). */
182
+ private makeThermoTwin;
157
183
  /** Light paint: the layer's light channel after the stroke, or `null` when the stroke has no twin or left it unchanged. */
158
184
  lightChannel(): TiledSurface | null;
185
+ /** UV-fluorescent ink: the layer's fluorescence channel after the stroke, or `null` when unchanged. */
186
+ fluorChannel(): TiledSurface | null;
187
+ /** Thermochromic ink: the layer's thermochromic channel after the stroke, or `null` when unchanged. */
188
+ thermoChannel(): TiledSurface | null;
159
189
  private tileTarget;
160
190
  get open(): boolean;
161
191
  /** Adds raw pen samples (document pixels). */
162
192
  push(points: readonly PenPoint[]): void;
163
193
  /** Sticker strokes: the stickers sprayed so far. */
164
194
  stickers(): Sticker[] | null;
165
- /** Impasto strokes: the layer's thick-paint channels with the stroke so far. */
195
+ /** Impasto or iridescent strokes: the layer's thick-paint channels with the stroke so far. */
166
196
  thickChannels(): ThickChannels | null;
167
197
  /** Shows a wet stroke's progress (the wet layer's display). */
168
198
  wetUpdate(rects: RasterRect[]): void;
@@ -15,10 +15,10 @@ import { type PaintBrush } from './brushes.js';
15
15
  import type { PaintGradientSource } from '../paint/gradient-paint.js';
16
16
  import type { EffectPaintPixels, PaintEffectPaint } from './effect-paint.js';
17
17
  import { type PenPoint, type StrokeCopy } from './engine.js';
18
- import type { PaperGrain } from './paper.js';
19
18
  import type { Affine } from './strokes.js';
20
19
  import type { WetStrokeDriver } from './wet.js';
21
20
  import { type ThickChannels } from './thick.js';
21
+ import { type PaperGrain } from './paper.js';
22
22
  import { type Sticker } from './stickers.js';
23
23
  import type { StickerArtLookup } from './sticker-art.js';
24
24
  import type { StickerRef, StickerVariant } from '../stickers/types.js';
@@ -100,6 +100,20 @@ export interface StrokeSessionInit {
100
100
  light?: {
101
101
  surface: TiledSurface;
102
102
  } | null;
103
+ /**
104
+ * UV-fluorescent ink: the layer's fluorescence channel before the stroke. With it, a twin of the
105
+ * stroke paints the 4-channel fluorescence surface; `fluorChannel()` returns the result.
106
+ */
107
+ fluor?: {
108
+ surface: TiledSurface;
109
+ } | null;
110
+ /**
111
+ * Thermochromic ink: the layer's thermochromic channel before the stroke. With it, a twin of the
112
+ * stroke paints the 4-channel thermochromic surface; `thermoChannel()` returns the result.
113
+ */
114
+ thermo?: {
115
+ surface: TiledSurface;
116
+ } | null;
103
117
  }
104
118
  export interface PaintStrokeResult {
105
119
  layerId: string;
@@ -129,10 +143,18 @@ export declare class PaintStrokeSession {
129
143
  private wet;
130
144
  private readonly wetDriver;
131
145
  private thickDriver;
146
+ private filmBuffer;
132
147
  private stickerSpray;
133
148
  /** Light paint: the stroke's twin over the light channel, and what it left there. */
134
149
  private readonly lightTwin;
135
150
  private lightResult;
151
+ /** UV-fluorescent ink: the stroke's twin over the fluorescence channel, and what it left there. */
152
+ private readonly fluorTwin;
153
+ private fluorResult;
154
+ /** Thermochromic ink: the stroke's twin over the thermochromic channel, and what it left there. */
155
+ private readonly thermoTwin;
156
+ private thermoResult;
157
+ private readonly invisibleDaylight;
136
158
  /** A wet stroke that ended while the simulation was mid-batch: it commits when the driver catches up. */
137
159
  private wetEndPending;
138
160
  private readonly apply;
@@ -154,15 +176,23 @@ export declare class PaintStrokeSession {
154
176
  repaintEffect(pixels: EffectPaintPixels): void;
155
177
  /** The stroke's twin over the light channel: the same marks in grey at the brush's light amount (see `light.ts`). */
156
178
  private makeLightTwin;
179
+ /** The stroke's twin over the 4-channel fluorescence surface (see `heat.ts`). */
180
+ private makeFluorTwin;
181
+ /** The stroke's twin over the 4-channel thermochromic surface (see `heat.ts`). */
182
+ private makeThermoTwin;
157
183
  /** Light paint: the layer's light channel after the stroke, or `null` when the stroke has no twin or left it unchanged. */
158
184
  lightChannel(): TiledSurface | null;
185
+ /** UV-fluorescent ink: the layer's fluorescence channel after the stroke, or `null` when unchanged. */
186
+ fluorChannel(): TiledSurface | null;
187
+ /** Thermochromic ink: the layer's thermochromic channel after the stroke, or `null` when unchanged. */
188
+ thermoChannel(): TiledSurface | null;
159
189
  private tileTarget;
160
190
  get open(): boolean;
161
191
  /** Adds raw pen samples (document pixels). */
162
192
  push(points: readonly PenPoint[]): void;
163
193
  /** Sticker strokes: the stickers sprayed so far. */
164
194
  stickers(): Sticker[] | null;
165
- /** Impasto strokes: the layer's thick-paint channels with the stroke so far. */
195
+ /** Impasto or iridescent strokes: the layer's thick-paint channels with the stroke so far. */
166
196
  thickChannels(): ThickChannels | null;
167
197
  /** Shows a wet stroke's progress (the wet layer's display). */
168
198
  wetUpdate(rects: RasterRect[]): void;
@@ -17,6 +17,7 @@ import { type MixerLoad, type PaintTileTarget, type RgbTriple } from '../paint/e
17
17
  import { type PaintGradientSource } from '../paint/gradient-paint.cjs';
18
18
  import { type EffectPaintPixels } from './effect-paint.cjs';
19
19
  import { TiledSurface, type RasterRect } from '../raster/surface.cjs';
20
+ import type { IridescenceSettings } from './brush-model.cjs';
20
21
  import type { PaintBrush } from './brushes.cjs';
21
22
  import { type PenPoint, type StrokeCopy } from './engine.cjs';
22
23
  /** Where the light comes from. */
@@ -30,26 +31,63 @@ export interface PaintLighting {
30
31
  }
31
32
  export declare const DEFAULT_LIGHTING: Readonly<PaintLighting>;
32
33
  export declare function normalizeLighting(raw?: Partial<PaintLighting>, base?: PaintLighting): PaintLighting;
34
+ /** View tilt (-1..1 on each axis, default 0): shifts the view vector for thin-film iridescence and highlights, not the light. Not saved with the document. */
35
+ export interface PaintViewTilt {
36
+ x: number;
37
+ y: number;
38
+ }
39
+ export declare const DEFAULT_VIEW_TILT: Readonly<PaintViewTilt>;
40
+ export declare function normalizeViewTilt(raw?: Partial<PaintViewTilt>, base?: PaintViewTilt): PaintViewTilt;
41
+ export declare function viewTiltKey(v: PaintViewTilt): string;
33
42
  /** A layer's thick-paint channels (layer space, like its pixels). */
34
43
  export interface ThickChannels {
35
44
  /** Height 0..255 per pixel (1 channel). */
36
45
  height: TiledSurface;
37
46
  /** Material per pixel (4 channels): R gloss, G metallic, B rim (light paint), A opacity for clarity (255 solid; 0 no material, read as solid). */
38
47
  material: TiledSurface;
48
+ /** Thin-film iridescence per pixel (4 channels): R thickness (0..255 → 100..1000 nm), G refractive index (0..255 → 1.2..2.0), B 0, A film coverage (0..255). */
49
+ film: TiledSurface;
39
50
  }
40
51
  export declare function emptyThickChannels(): ThickChannels;
52
+ export declare function hasThickChannels(ch: ThickChannels | undefined): boolean;
53
+ /**
54
+ * Thin-film interference reflectance (linear RGB 0..1) for an optical path difference `opd` in nm:
55
+ * Airy / cosine interference across the three primary bands (630, 532, 465 nm) with a half-wave
56
+ * top-surface phase shift.
57
+ */
58
+ export declare function thinFilmRgb(opd: number): [number, number, number];
41
59
  export declare function lightingKey(l: PaintLighting): string;
42
60
  /**
43
- * One tile of a thick layer lit: `color` (RGBA, straight), the height and material of the tile, and
44
- * the height of its 8 neighbours for the slopes at the border (`heightAt(dx, dy)` returns the
45
- * neighbouring tile's data or null). Returns a new RGBA tile.
61
+ * One tile of a thick or iridescent layer lit: `color` (RGBA, straight), the height, material and
62
+ * thin-film channels of the tile, and the height of its 8 neighbours for the slopes at the border
63
+ * (`heightAround(dx, dy)` returns the neighbouring tile's data or null). Returns a new RGBA tile.
46
64
  */
47
- export declare function lightTile(color: Uint8ClampedArray, height: Uint8ClampedArray | null, heightAround: (dx: number, dy: number) => Uint8ClampedArray | null, material: Uint8ClampedArray | null, light: PaintLighting): Uint8ClampedArray;
65
+ export declare function lightTile(color: Uint8ClampedArray, height: Uint8ClampedArray | null, heightAround: (dx: number, dy: number) => Uint8ClampedArray | null, material: Uint8ClampedArray | null, light: PaintLighting, film?: Uint8ClampedArray | null, viewTilt?: PaintViewTilt): Uint8ClampedArray;
48
66
  /** Cache of lit tiles for one layer, by the identity of their inputs. */
49
67
  export declare class LitCache {
50
68
  private readonly tiles;
51
- /** The layer's pixels lit (tiles without relief nearby are the colour tiles themselves). */
52
- lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting): TiledSurface;
69
+ /** The layer's pixels lit (tiles without relief or film nearby are the colour tiles themselves). */
70
+ lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting, viewTilt?: PaintViewTilt): TiledSurface;
71
+ }
72
+ /** Live thin-film iridescence buffer for any stroke engine (deposits or erases into 4-channel `ThickChannels.film`). */
73
+ export declare class FilmBuffer {
74
+ private readonly base;
75
+ private readonly offset;
76
+ private readonly mask;
77
+ private readonly tiles;
78
+ constructor(base: TiledSurface, offset?: {
79
+ x: number;
80
+ y: number;
81
+ }, mask?: PaintTileTarget['selection']);
82
+ private maskAt;
83
+ private tile;
84
+ /** Deposits thin-film iridescence over `rect` with per-pixel `coverage` (0..1) scaled by `strength` (0..1). */
85
+ deposit(rect: RasterRect, coverage: ArrayLike<number>, strength: number, ir: IridescenceSettings, pressure?: number): void;
86
+ /** Stamps a soft disc of thin-film iridescence at `(cx, cy)` (document pixels) with `radius` and `alpha` (0..1). */
87
+ stampDisc(cx: number, cy: number, radius: number, alpha: number, ir: IridescenceSettings, pressure?: number): void;
88
+ /** Erases thin-film coverage over `rect` by `coverage[p] * strength`. */
89
+ erase(rect: RasterRect, coverage: ArrayLike<number>, strength: number): void;
90
+ surface(): TiledSurface;
53
91
  }
54
92
  export interface ThickStrokeInit {
55
93
  brush: PaintBrush;
@@ -76,6 +114,7 @@ export declare class ThickStrokeDriver {
76
114
  private readonly medium;
77
115
  private readonly height;
78
116
  private readonly material;
117
+ private readonly film;
79
118
  private readonly paint;
80
119
  private readonly centre;
81
120
  private tip;
@@ -119,8 +158,10 @@ export interface SavedThickChannels {
119
158
  height: string;
120
159
  /** RGBA PNG of the material (R gloss, G metallic). */
121
160
  material: string;
161
+ /** RGBA PNG of the thin-film iridescence (R thickness, G refractive index, A coverage), when any. */
162
+ film?: string;
122
163
  }
123
- /** Channels → saved form (`null` when there is no relief). */
164
+ /** Channels → saved form (`null` when there is neither relief nor film). */
124
165
  export declare function thickToJSON(ch: ThickChannels): SavedThickChannels | null;
125
166
  /** Saved form → channels; throws a sentence when it cannot be read. */
126
167
  export declare function thickFromJSON(raw: unknown): ThickChannels;
@@ -17,6 +17,7 @@ import { type MixerLoad, type PaintTileTarget, type RgbTriple } from '../paint/e
17
17
  import { type PaintGradientSource } from '../paint/gradient-paint.js';
18
18
  import { type EffectPaintPixels } from './effect-paint.js';
19
19
  import { TiledSurface, type RasterRect } from '../raster/surface.js';
20
+ import type { IridescenceSettings } from './brush-model.js';
20
21
  import type { PaintBrush } from './brushes.js';
21
22
  import { type PenPoint, type StrokeCopy } from './engine.js';
22
23
  /** Where the light comes from. */
@@ -30,26 +31,63 @@ export interface PaintLighting {
30
31
  }
31
32
  export declare const DEFAULT_LIGHTING: Readonly<PaintLighting>;
32
33
  export declare function normalizeLighting(raw?: Partial<PaintLighting>, base?: PaintLighting): PaintLighting;
34
+ /** View tilt (-1..1 on each axis, default 0): shifts the view vector for thin-film iridescence and highlights, not the light. Not saved with the document. */
35
+ export interface PaintViewTilt {
36
+ x: number;
37
+ y: number;
38
+ }
39
+ export declare const DEFAULT_VIEW_TILT: Readonly<PaintViewTilt>;
40
+ export declare function normalizeViewTilt(raw?: Partial<PaintViewTilt>, base?: PaintViewTilt): PaintViewTilt;
41
+ export declare function viewTiltKey(v: PaintViewTilt): string;
33
42
  /** A layer's thick-paint channels (layer space, like its pixels). */
34
43
  export interface ThickChannels {
35
44
  /** Height 0..255 per pixel (1 channel). */
36
45
  height: TiledSurface;
37
46
  /** Material per pixel (4 channels): R gloss, G metallic, B rim (light paint), A opacity for clarity (255 solid; 0 no material, read as solid). */
38
47
  material: TiledSurface;
48
+ /** Thin-film iridescence per pixel (4 channels): R thickness (0..255 → 100..1000 nm), G refractive index (0..255 → 1.2..2.0), B 0, A film coverage (0..255). */
49
+ film: TiledSurface;
39
50
  }
40
51
  export declare function emptyThickChannels(): ThickChannels;
52
+ export declare function hasThickChannels(ch: ThickChannels | undefined): boolean;
53
+ /**
54
+ * Thin-film interference reflectance (linear RGB 0..1) for an optical path difference `opd` in nm:
55
+ * Airy / cosine interference across the three primary bands (630, 532, 465 nm) with a half-wave
56
+ * top-surface phase shift.
57
+ */
58
+ export declare function thinFilmRgb(opd: number): [number, number, number];
41
59
  export declare function lightingKey(l: PaintLighting): string;
42
60
  /**
43
- * One tile of a thick layer lit: `color` (RGBA, straight), the height and material of the tile, and
44
- * the height of its 8 neighbours for the slopes at the border (`heightAt(dx, dy)` returns the
45
- * neighbouring tile's data or null). Returns a new RGBA tile.
61
+ * One tile of a thick or iridescent layer lit: `color` (RGBA, straight), the height, material and
62
+ * thin-film channels of the tile, and the height of its 8 neighbours for the slopes at the border
63
+ * (`heightAround(dx, dy)` returns the neighbouring tile's data or null). Returns a new RGBA tile.
46
64
  */
47
- export declare function lightTile(color: Uint8ClampedArray, height: Uint8ClampedArray | null, heightAround: (dx: number, dy: number) => Uint8ClampedArray | null, material: Uint8ClampedArray | null, light: PaintLighting): Uint8ClampedArray;
65
+ export declare function lightTile(color: Uint8ClampedArray, height: Uint8ClampedArray | null, heightAround: (dx: number, dy: number) => Uint8ClampedArray | null, material: Uint8ClampedArray | null, light: PaintLighting, film?: Uint8ClampedArray | null, viewTilt?: PaintViewTilt): Uint8ClampedArray;
48
66
  /** Cache of lit tiles for one layer, by the identity of their inputs. */
49
67
  export declare class LitCache {
50
68
  private readonly tiles;
51
- /** The layer's pixels lit (tiles without relief nearby are the colour tiles themselves). */
52
- lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting): TiledSurface;
69
+ /** The layer's pixels lit (tiles without relief or film nearby are the colour tiles themselves). */
70
+ lit(color: TiledSurface, ch: ThickChannels, light: PaintLighting, viewTilt?: PaintViewTilt): TiledSurface;
71
+ }
72
+ /** Live thin-film iridescence buffer for any stroke engine (deposits or erases into 4-channel `ThickChannels.film`). */
73
+ export declare class FilmBuffer {
74
+ private readonly base;
75
+ private readonly offset;
76
+ private readonly mask;
77
+ private readonly tiles;
78
+ constructor(base: TiledSurface, offset?: {
79
+ x: number;
80
+ y: number;
81
+ }, mask?: PaintTileTarget['selection']);
82
+ private maskAt;
83
+ private tile;
84
+ /** Deposits thin-film iridescence over `rect` with per-pixel `coverage` (0..1) scaled by `strength` (0..1). */
85
+ deposit(rect: RasterRect, coverage: ArrayLike<number>, strength: number, ir: IridescenceSettings, pressure?: number): void;
86
+ /** Stamps a soft disc of thin-film iridescence at `(cx, cy)` (document pixels) with `radius` and `alpha` (0..1). */
87
+ stampDisc(cx: number, cy: number, radius: number, alpha: number, ir: IridescenceSettings, pressure?: number): void;
88
+ /** Erases thin-film coverage over `rect` by `coverage[p] * strength`. */
89
+ erase(rect: RasterRect, coverage: ArrayLike<number>, strength: number): void;
90
+ surface(): TiledSurface;
53
91
  }
54
92
  export interface ThickStrokeInit {
55
93
  brush: PaintBrush;
@@ -76,6 +114,7 @@ export declare class ThickStrokeDriver {
76
114
  private readonly medium;
77
115
  private readonly height;
78
116
  private readonly material;
117
+ private readonly film;
79
118
  private readonly paint;
80
119
  private readonly centre;
81
120
  private tip;
@@ -119,8 +158,10 @@ export interface SavedThickChannels {
119
158
  height: string;
120
159
  /** RGBA PNG of the material (R gloss, G metallic). */
121
160
  material: string;
161
+ /** RGBA PNG of the thin-film iridescence (R thickness, G refractive index, A coverage), when any. */
162
+ film?: string;
122
163
  }
123
- /** Channels → saved form (`null` when there is no relief). */
164
+ /** Channels → saved form (`null` when there is neither relief nor film). */
124
165
  export declare function thickToJSON(ch: ThickChannels): SavedThickChannels | null;
125
166
  /** Saved form → channels; throws a sentence when it cannot be read. */
126
167
  export declare function thickFromJSON(raw: unknown): ThickChannels;
@@ -156,7 +156,7 @@ export declare function guideOverlay(controller: PaintEditorController, drag?: {
156
156
  to: Point2;
157
157
  kind: GuideOverlayKind;
158
158
  }>;
159
- export { BRUSH_SLIDERS, brushEngineControls, GHOST_SLIDERS, multiColorSliders, patchBrushValue, type BrushEngineControls, type SliderControl } from './brush-controls.cjs';
159
+ export { BRUSH_SLIDERS, brushEngineControls, GHOST_SLIDERS, IRIDESCENCE_SLIDERS, multiColorSliders, MUTATION_SLIDERS, patchBrushValue, type BrushEngineControls, type SliderControl } from './brush-controls.cjs';
160
160
  import type { SliderControl } from './brush-controls.cjs';
161
161
  export declare const PAPER_SLIDERS: readonly SliderControl[];
162
162
  /** Reads `a.b` from an object. */
@@ -230,6 +230,39 @@ export declare const WET_MEDIUM_LABELS: Readonly<Record<WetMediumKind, string>>;
230
230
  export declare const IMPASTO_TOOL_LABELS: Readonly<Record<ImpastoTool, string>>;
231
231
  /** The light sliders (thick paint and metallic paper). */
232
232
  export declare const LIGHT_SLIDERS: readonly SliderControl[];
233
+ /** The UV blacklight sliders. */
234
+ export declare const UV_LIGHT_SLIDERS: readonly SliderControl[];
235
+ /** The view tilt sliders (thin-film iridescence and specular highlights). */
236
+ export declare const VIEW_TILT_SLIDERS: readonly SliderControl[];
237
+ /**
238
+ * Maps `DeviceOrientationEvent` angles (`beta` front-to-back in degrees, `gamma` left-to-right in
239
+ * degrees) relative to `rest` (default `{ beta: 45, gamma: 0 }`, a tablet held in hand) into a
240
+ * `viewTilt` in `-1..1` (35° reaches full tilt), rounded to 0.01.
241
+ */
242
+ export declare function deviceOrientationToViewTilt(betaOrEvent: number | {
243
+ beta?: number | null;
244
+ gamma?: number | null;
245
+ } | null | undefined, gammaOrRest?: number | {
246
+ beta: number;
247
+ gamma: number;
248
+ } | null | undefined, rest?: {
249
+ beta: number;
250
+ gamma: number;
251
+ }): {
252
+ x: number;
253
+ y: number;
254
+ };
255
+ /**
256
+ * Asks iOS Safari for `DeviceOrientationEvent` permission when `requestPermission` is present
257
+ * (must be called from a user gesture on iOS 13+). Resolves to `'granted'` on browsers that do not
258
+ * gate `deviceorientation` behind a permission prompt, or `'unsupported'` without `DeviceOrientationEvent`.
259
+ */
260
+ export declare function requestDeviceTiltPermission(): Promise<'granted' | 'denied' | 'unsupported'>;
261
+ /**
262
+ * Listens to `deviceorientation` events on `globalThis` and updates `controller.setViewTilt` relative
263
+ * to the first reading's orientation. Returns a cleanup function.
264
+ */
265
+ export declare function startDeviceTilt(controller: PaintEditorController): () => void;
233
266
  /** Labels of the particle kinds and renders. */
234
267
  export declare const PARTICLE_KIND_LABELS: Readonly<Record<ParticleKind, string>>;
235
268
  export declare const PARTICLE_RENDER_LABELS: Readonly<Record<ParticleRender, string>>;