@pixerate/editor 0.222.1 → 0.222.3
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/package.json +1 -1
- package/skills/README.md +8 -1
- package/skills/pixerate-paint-editor/SKILL.md +20 -59
- package/skills/pixerate-paint-editor/references/oils.md +19 -0
- package/skills/pixerate-paint-editor/references/wet-media.md +35 -0
- package/skills/pixerate-paint-oil-impasto/SKILL.md +103 -0
- package/skills/pixerate-paint-oil-impasto/evals/impasto-highlights.json +24 -0
- package/skills/pixerate-paint-oil-impasto/evals/lean-to-thick.json +17 -0
- package/skills/pixerate-paint-oil-impasto/evals/mix-green.json +15 -0
- package/skills/pixerate-paint-sketch-to-finish/SKILL.md +97 -0
- package/skills/pixerate-paint-sketch-to-finish/evals/clipped-shading.json +26 -0
- package/skills/pixerate-paint-sketch-to-finish/evals/flats-under-ink.json +27 -0
- package/skills/pixerate-paint-sketch-to-finish/evals/sketch-ink-hide.json +20 -0
- package/skills/pixerate-paint-watercolour/SKILL.md +106 -0
- package/skills/pixerate-paint-watercolour/evals/glaze-over-dry.json +24 -0
- package/skills/pixerate-paint-watercolour/evals/paper-and-brush-for-detail.json +16 -0
- package/skills/pixerate-paint-watercolour/evals/sky-wash.json +19 -0
- package/skills/pixerate-video-colour-grade/SKILL.md +105 -0
- package/skills/pixerate-video-colour-grade/evals/look-in-one-place.json +16 -0
- package/skills/pixerate-video-colour-grade/evals/lut-keep-skin.json +132 -0
- package/skills/pixerate-video-colour-grade/evals/match-exposure.json +14 -0
- package/skills/pixerate-video-editor/SKILL.md +3 -0
- package/skills/pixerate-video-montage/SKILL.md +99 -0
- package/skills/pixerate-video-montage/evals/assemble-on-beats.json +231 -0
- package/skills/pixerate-video-montage/evals/music-and-ending.json +210 -0
- package/skills/pixerate-video-montage/evals/short-crossfades.json +190 -0
- package/skills/pixerate-video-social-cut/SKILL.md +105 -0
- package/skills/pixerate-video-social-cut/evals/hook-first.json +19 -0
- package/skills/pixerate-video-social-cut/evals/opening-title.json +14 -0
- package/skills/pixerate-video-social-cut/evals/vertical-reframe.json +15 -0
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -47,6 +47,12 @@ request like "clean up this export" finds it.
|
|
|
47
47
|
| [`pixerate-vector-icon-set`](pixerate-vector-icon-set/SKILL.md) | Vector (`pixerate-vector`) | Icons on one grid with padding, one stroke, cap, join and radius, crisp coordinates, clean paths, symbols and SVG export. |
|
|
48
48
|
| [`pixerate-vector-logo`](pixerate-vector-logo/SKILL.md) | Vector (`pixerate-vector`) | Marks from primitives with live booleans, optical corrections, an aligned lockup, colour variants as symbols, outlined delivery files. |
|
|
49
49
|
| [`pixerate-vector-illustration`](pixerate-vector-illustration/SKILL.md) | Vector (`pixerate-vector`) | Block in back to front, gradients and stacked fills for form, masks, blends, subtle shadows with one light, export. |
|
|
50
|
+
| [`pixerate-paint-watercolour`](pixerate-paint-watercolour/SKILL.md) | Paint (`pixerate-paint-editor`) | Watercolour paper, light to dark with the paper's white kept, wet-in-wet and wet-on-dry washes, drying before glazing, steering the water. |
|
|
51
|
+
| [`pixerate-paint-oil-impasto`](pixerate-paint-oil-impasto/SKILL.md) | Paint (`pixerate-paint-editor`) | A toned ground, thin block-in to thick paint, pigment mixing, impasto relief and its light. |
|
|
52
|
+
| [`pixerate-paint-sketch-to-finish`](pixerate-paint-sketch-to-finish/SKILL.md) | Paint (`pixerate-paint-editor`) | Sketch, ink, flats under the lines, clipped shading and light, one named layer per pass. |
|
|
53
|
+
| [`pixerate-video-social-cut`](pixerate-video-social-cut/SKILL.md) | Video (`pixerate-video-editor`) | Vertical or square reframes that fill the frame, the hook first, platform lengths, titles in the safe area. |
|
|
54
|
+
| [`pixerate-video-colour-grade`](pixerate-video-colour-grade/SKILL.md) | Video (`pixerate-video-editor`) | Correct and match shots, then one look on an adjustment clip; LUTs at moderate strength with skin protected. |
|
|
55
|
+
| [`pixerate-video-montage`](pixerate-video-montage/SKILL.md) | Video (`pixerate-video-editor`) | Selects on the beat, varied pacing, short transitions with handles, music under everything and a clean ending. |
|
|
50
56
|
| [`pixerate-talking-head-edit`](pixerate-talking-head-edit/SKILL.md) | Video (`pixerate-video-editor`) | Talking-head cleanup by transcript: fillers, long pauses and stutters found in one pass and cut as one plan reviewed before it applies. Owns the dialogue tools. |
|
|
51
57
|
| [`pixerate-bgm-alignment`](pixerate-bgm-alignment/SKILL.md) | Video (`pixerate-video-editor`) | Background music: beats and cuts on the beat, ducking under speech, loudness in LUFS. Owns the music tools. |
|
|
52
58
|
|
|
@@ -110,7 +116,8 @@ same change.
|
|
|
110
116
|
`packages/core/tests/agent-skill-evals.test.ts`. An eval has a `prompt` (the task, in the
|
|
111
117
|
user's words), a `fixture` (the starting document: `{ "editor": "spreadsheet", "rows" }`,
|
|
112
118
|
`{ "editor": "image", "width", "height", "background", "shapes" }` or `{ "editor":
|
|
113
|
-
"vector", "width", "height", "setup" }` with commands that draw the starting art
|
|
119
|
+
"vector", "width", "height", "setup" }` with commands that draw the starting art,
|
|
120
|
+
`{ "editor": "paint", "width", "height", "setup" }` or `{ "editor": "video", "setup" }` with tool calls), `checks` (a read-only
|
|
114
121
|
tool, or `run_command` with a read-only command such as `vector.list`, its `args`, what its result must match in `expect`, and `why`) and a `solution` (the
|
|
115
122
|
tool calls an expert would make). The test proves each eval: some check fails on the fixture,
|
|
116
123
|
and all pass after the solution. A model harness runs the same file with the model in place
|
|
@@ -47,6 +47,15 @@ is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y do
|
|
|
47
47
|
- **Undo**: one tool call that changes the painting is one undo step (`undo_paint`). Brush,
|
|
48
48
|
colour, view, hand and the Mixer Brush load are editor state, not undo steps.
|
|
49
49
|
|
|
50
|
+
## Workflow skills
|
|
51
|
+
|
|
52
|
+
For these jobs, load the workflow skill too: it has the method, the checks between steps and
|
|
53
|
+
the pitfalls, and uses the tools below.
|
|
54
|
+
|
|
55
|
+
- `pixerate-paint-watercolour`: paper, light to dark, wet-in-wet and wet-on-dry, drying and glazing.
|
|
56
|
+
- `pixerate-paint-oil-impasto`: toned ground, thin to thick, pigment mixing, impasto and its light.
|
|
57
|
+
- `pixerate-paint-sketch-to-finish`: sketch, ink, flats, clipped shading and light, one layer per pass.
|
|
58
|
+
|
|
50
59
|
## Working method
|
|
51
60
|
|
|
52
61
|
1. **Read.** `get_canvas_info` (size, paper, active layer, active brush, colour), then
|
|
@@ -82,8 +91,7 @@ is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y do
|
|
|
82
91
|
`repeat` for hatching, grass, foliage, rows, fans or spirals, in one undo step.
|
|
83
92
|
8. **Check** with `render_preview` after each block of work, not after each stroke.
|
|
84
93
|
`pick_color` reads exact values. Undo what made it worse.
|
|
85
|
-
9. **Watercolour** is its own way of working
|
|
86
|
-
lay washes light to dark, let them flow (`advance_wet_paint`), dry them (`dry_paint`), glaze.
|
|
94
|
+
9. **Watercolour** is its own way of working: see "Wet media" below.
|
|
87
95
|
10. **Deliver**: `export_image` (PNG, or JPEG / WebP in a browser), `export_psd` for Photoshop,
|
|
88
96
|
`save_paint_document` to keep the painting with its strokes (when the host has storage).
|
|
89
97
|
|
|
@@ -133,71 +141,24 @@ is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y do
|
|
|
133
141
|
|
|
134
142
|
PSD has none of them: `export_psd` writes the nearest standard mode (watercolor and shadow
|
|
135
143
|
as Multiply, tint as Color, highlight as Screen) and warns; the merged image stays exact.
|
|
136
|
-
- **Grow the canvas instead of starting big
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
the returned `rect` (the new canvas in the old coordinates). Wet paint dries first, so finish
|
|
141
|
-
or dry a wash (`dry_paint`) before extending. `set_canvas` only crops or grows at the
|
|
142
|
-
bottom-right and never moves the picture.
|
|
144
|
+
- **Grow the canvas instead of starting big** with `extend_canvas` (pixels per side; nothing
|
|
145
|
+
painted moves relative to anything else). Adding `left` or `top` shifts every coordinate you
|
|
146
|
+
hold by (+left, +top): re-read `list_layers` or use the returned `rect`. Dry wet paint first.
|
|
147
|
+
`set_canvas` only crops or grows at the bottom-right.
|
|
143
148
|
- **Save brushes you build** (`set_brush` then `save_brush`), and share them with
|
|
144
149
|
`export_brushes` / `import_brushes`.
|
|
145
150
|
|
|
146
151
|
## Wet media
|
|
147
152
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`gouache`, body 1, for that). Lay the lightest washes first.
|
|
153
|
-
3. **Wet-in-wet or wet-on-dry.** `wet_paper` over an area first for soft, feathered, blooming
|
|
154
|
-
colour (skies, backgrounds); paint on dry paper for crisp edges with a darker rim.
|
|
155
|
-
4. **Paint washes** with `watercolor-wash` (large, flat, very wet) in a few long strokes, then
|
|
156
|
-
shapes with `watercolor-round`, details with `watercolor-detail`. Colours touching while wet
|
|
157
|
-
bleed into each other; that is the point.
|
|
158
|
-
5. **Let it flow, then dry.** `advance_wet_paint` (`seconds` 1..5) to let water spread, run and
|
|
159
|
-
bloom; `render_preview` shows the paint as it is now (wet paper looks a little darker).
|
|
160
|
-
`dry_paint` sets it where it is. Dry before glazing a second wash over a first, or the two
|
|
161
|
-
mix instead of layering.
|
|
162
|
-
6. **Steer the water** with the wet tools: `add-water` (softens and lifts with `rewet`),
|
|
163
|
-
`hair-dryer` (stops a wash spreading), `blow` (pushes the water along the stroke),
|
|
164
|
-
`wet-blend` (evens out a wash), `wet-drag` (drags wet paint), `blot` (lifts wet paint: clouds,
|
|
165
|
-
highlights). `set_canvas_tilt` makes washes run downhill and very wet paint drip;
|
|
166
|
-
`set_flow_map` pushes water in a swirl, a direction or eddies.
|
|
167
|
-
7. **Tune the medium** per layer with `set_wet_settings`: `granulation` (grainy pigment, needs a
|
|
168
|
-
rough paper), `edgeDarkening`, `bloom`, `bleed`, `viscosity` (runny .. thick), `evaporation`,
|
|
169
|
-
`pigmentWeight`, `pickup` (how easily dried paint lifts), `fixture` (staining).
|
|
170
|
-
|
|
171
|
-
- **Saving dries the paint** (deterministically): the saved file and every export hold dried
|
|
172
|
-
pixels; the editor keeps painting wet. A dry brush, fill, merge or duplicate on a wet layer
|
|
173
|
-
dries it first (in the same undo step).
|
|
174
|
-
- **Time** runs by itself in an editor on screen (`pause_wet_paint` stops it); for an agent with
|
|
175
|
-
no screen, nothing moves until `advance_wet_paint` or `dry_paint`.
|
|
176
|
-
- **The GPU only draws.** With WebGPU and a large wet area (16 tiles of 256 px by default), Engine B
|
|
177
|
-
shades the wet paint on the GPU; the CPU engine still computes every step, so the painting,
|
|
178
|
-
`dry_paint`, saving, export and replay come out the same with it on or off. `get_wet_paint`
|
|
179
|
-
shows it as `engine` (`state`, `reason`). Leave it on `auto`; `set_wet_engine` with `gpu` "off"
|
|
180
|
-
only when the user says the display looks wrong or the GPU is needed for something else.
|
|
153
|
+
Watercolour, gouache and wet oil run on a fluid simulation: lay washes light to dark, let them
|
|
154
|
+
flow (`advance_wet_paint`), dry them (`dry_paint`) before glazing. Nothing moves for an agent
|
|
155
|
+
until you advance or dry it, and saving dries the paint. Load `pixerate-paint-watercolour`, and
|
|
156
|
+
read [references/wet-media.md](references/wet-media.md) for the engine and the wet tools.
|
|
181
157
|
|
|
182
158
|
## Oils and thick paint
|
|
183
159
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
time, and the brush picks up and mixes the wet oil it meets (`behavior.pickup` / `mix`), so
|
|
187
|
-
colours blend on the canvas. A layer takes the medium's wet settings the first time it holds
|
|
188
|
-
wet paint; keep oil and watercolour on separate layers. `set_instant_dry` on makes every wet
|
|
189
|
-
stroke dry as it ends (strokes stay exactly as laid).
|
|
190
|
-
- **Thick paint** (`engine: "impasto"`, category `impasto`) gives a layer relief (height) and
|
|
191
|
-
material (gloss, metallic) next to its colour, lit at render time by `set_lighting`
|
|
192
|
-
(azimuth, elevation, relief). Tools: `impasto-brush` (bristle ridges), `paint-tube` (blobs),
|
|
193
|
-
`palette-knife` (scrapes the relief flat and smears the paint), `paint-roller` (flat stippled
|
|
194
|
-
band), `gloop-pen` (glossy bead), `metallic-paint` (highlights in the paint's colour).
|
|
195
|
-
- **Loading and thinner**: `wet.load` / `impasto.load` below 1 make the brush run out along a
|
|
196
|
-
long stroke (dry-brush ends); `thinner` makes paint flatter and more transparent.
|
|
197
|
-
- **Light** is a view of the relief, not paint: move it (`set_lighting`) to read the texture
|
|
198
|
-
(a low elevation, 15..30°, shows brushwork best) and pick the final light before exporting.
|
|
199
|
-
`get_relief` reads a layer's relief; `flatten_relief` removes it but keeps the colour.
|
|
200
|
-
- **Metallic paper**: `set_paper` `metallic` 0..1 makes the grain sparkle under the light.
|
|
160
|
+
Wet oil barely flows and blends where the brush meets it; impasto brushes add relief lit by
|
|
161
|
+
`set_lighting`. Load `pixerate-paint-oil-impasto`, and read [references/oils.md](references/oils.md).
|
|
201
162
|
|
|
202
163
|
## Gradient paint
|
|
203
164
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Oils and thick paint
|
|
2
|
+
|
|
3
|
+
- **Wet oil** (`oil-round`, `oil-flat`; any wet brush with `wet.medium: "oil"`) runs on the wet
|
|
4
|
+
simulation as a thick, slow-drying medium: it barely flows, stays wet for minutes of simulated
|
|
5
|
+
time, and the brush picks up and mixes the wet oil it meets (`behavior.pickup` / `mix`), so
|
|
6
|
+
colours blend on the canvas. A layer takes the medium's wet settings the first time it holds
|
|
7
|
+
wet paint; keep oil and watercolour on separate layers. `set_instant_dry` on makes every wet
|
|
8
|
+
stroke dry as it ends (strokes stay exactly as laid).
|
|
9
|
+
- **Thick paint** (`engine: "impasto"`, category `impasto`) gives a layer relief (height) and
|
|
10
|
+
material (gloss, metallic) next to its colour, lit at render time by `set_lighting`
|
|
11
|
+
(azimuth, elevation, relief). Tools: `impasto-brush` (bristle ridges), `paint-tube` (blobs),
|
|
12
|
+
`palette-knife` (scrapes the relief flat and smears the paint), `paint-roller` (flat stippled
|
|
13
|
+
band), `gloop-pen` (glossy bead), `metallic-paint` (highlights in the paint's colour).
|
|
14
|
+
- **Loading and thinner**: `wet.load` / `impasto.load` below 1 make the brush run out along a
|
|
15
|
+
long stroke (dry-brush ends); `thinner` makes paint flatter and more transparent.
|
|
16
|
+
- **Light** is a view of the relief, not paint: move it (`set_lighting`) to read the texture
|
|
17
|
+
(a low elevation, 15..30°, shows brushwork best) and pick the final light before exporting.
|
|
18
|
+
`get_relief` reads a layer's relief; `flatten_relief` removes it but keeps the colour.
|
|
19
|
+
- **Metallic paper**: `set_paper` `metallic` 0..1 makes the grain sparkle under the light.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Wet media
|
|
2
|
+
|
|
3
|
+
1. **Choose a watercolour paper** (`cold-pressed`, `rough-watercolor` for granulation and broken
|
|
4
|
+
edges, `hot-pressed` for smooth washes). Absorption, permeability and roughness of the paper
|
|
5
|
+
drive the simulation.
|
|
6
|
+
2. **Plan light to dark.** Watercolour is transparent: you cannot paint light over dark (use
|
|
7
|
+
`gouache`, body 1, for that). Lay the lightest washes first.
|
|
8
|
+
3. **Wet-in-wet or wet-on-dry.** `wet_paper` over an area first for soft, feathered, blooming
|
|
9
|
+
colour (skies, backgrounds); paint on dry paper for crisp edges with a darker rim.
|
|
10
|
+
4. **Paint washes** with `watercolor-wash` (large, flat, very wet) in a few long strokes, then
|
|
11
|
+
shapes with `watercolor-round`, details with `watercolor-detail`. Colours touching while wet
|
|
12
|
+
bleed into each other; that is the point.
|
|
13
|
+
5. **Let it flow, then dry.** `advance_wet_paint` (`seconds` 1..5) to let water spread, run and
|
|
14
|
+
bloom; `render_preview` shows the paint as it is now (wet paper looks a little darker).
|
|
15
|
+
`dry_paint` sets it where it is. Dry before glazing a second wash over a first, or the two
|
|
16
|
+
mix instead of layering.
|
|
17
|
+
6. **Steer the water** with the wet tools: `add-water` (softens and lifts with `rewet`),
|
|
18
|
+
`hair-dryer` (stops a wash spreading), `blow` (pushes the water along the stroke),
|
|
19
|
+
`wet-blend` (evens out a wash), `wet-drag` (drags wet paint), `blot` (lifts wet paint: clouds,
|
|
20
|
+
highlights). `set_canvas_tilt` makes washes run downhill and very wet paint drip;
|
|
21
|
+
`set_flow_map` pushes water in a swirl, a direction or eddies.
|
|
22
|
+
7. **Tune the medium** per layer with `set_wet_settings`: `granulation` (grainy pigment, needs a
|
|
23
|
+
rough paper), `edgeDarkening`, `bloom`, `bleed`, `viscosity` (runny .. thick), `evaporation`,
|
|
24
|
+
`pigmentWeight`, `pickup` (how easily dried paint lifts), `fixture` (staining).
|
|
25
|
+
|
|
26
|
+
- **Saving dries the paint** (deterministically): the saved file and every export hold dried
|
|
27
|
+
pixels; the editor keeps painting wet. A dry brush, fill, merge or duplicate on a wet layer
|
|
28
|
+
dries it first (in the same undo step).
|
|
29
|
+
- **Time** runs by itself in an editor on screen (`pause_wet_paint` stops it); for an agent with
|
|
30
|
+
no screen, nothing moves until `advance_wet_paint` or `dry_paint`.
|
|
31
|
+
- **The GPU only draws.** With WebGPU and a large wet area (16 tiles of 256 px by default), Engine B
|
|
32
|
+
shades the wet paint on the GPU; the CPU engine still computes every step, so the painting,
|
|
33
|
+
`dry_paint`, saving, export and replay come out the same with it on or off. `get_wet_paint`
|
|
34
|
+
shows it as `engine` (`state`, `reason`). Leave it on `auto`; `set_wet_engine` with `gpu` "off"
|
|
35
|
+
only when the user says the display looks wrong or the GPU is needed for something else.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-paint-oil-impasto
|
|
3
|
+
description: The oil and impasto workflow in the Pixerate paint editor - a toned ground on canvas, blocking in thin and building to thick (fat over lean), mixing colours as real pigment, blending wet oil on the canvas, thick impasto paint with relief from brushes, tubes and palette knives, lighting the relief, and keeping thick passages on their own layers. Use when asked to paint in oils or acrylics, paint thickly, add impasto or texture, use a palette knife, mix paint, block in a painting, or paint with visible brushwork, or when set_lighting, get_relief, mix_colors or the oil, impasto and bristle brushes are available. Load with pixerate-paint-editor, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-paint-editor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Painting in oils and impasto
|
|
10
|
+
|
|
11
|
+
An oil painting is built in passes: a toned ground, a thin block-in of the big shapes and
|
|
12
|
+
values, then thicker, more opaque paint for the forms, and the thickest paint last for the
|
|
13
|
+
lights. Wet oil (`oil-round`, `oil-flat`) barely flows and stays wet, so the brush picks up and
|
|
14
|
+
mixes what it meets. Thick paint (the `impasto` brushes) also gives the layer relief, which a
|
|
15
|
+
movable light shows at render time.
|
|
16
|
+
|
|
17
|
+
This skill is the method. `pixerate-paint-editor` has the full tool reference and
|
|
18
|
+
`references/oils.md` the media's detail.
|
|
19
|
+
|
|
20
|
+
## Goal and quality bar
|
|
21
|
+
|
|
22
|
+
- Lean to fat: thin paint first, thick paint on top; the thickest paint in the lights.
|
|
23
|
+
- Colours mixed as pigment (`mix_colors` predicts Kubelka-Munk mixes: blue and yellow make
|
|
24
|
+
green, white tints), from a small palette.
|
|
25
|
+
- Relief where it reads: highlights and focal areas thick, shadows and background thin
|
|
26
|
+
(`get_relief` `maxHeight` on the thick layer).
|
|
27
|
+
- One light on the relief, chosen deliberately (`set_lighting`), and the thick passages on
|
|
28
|
+
their own layers so they can be changed.
|
|
29
|
+
|
|
30
|
+
## Inputs to confirm
|
|
31
|
+
|
|
32
|
+
- **Subject and palette.**
|
|
33
|
+
- **Texture:** smooth and blended, or heavy impasto with visible brushwork and knife marks.
|
|
34
|
+
- **Light direction** for the relief, if the user has one in mind (default top-left).
|
|
35
|
+
|
|
36
|
+
## Working method
|
|
37
|
+
|
|
38
|
+
1. **Ground.** `set_paper` `cotton-canvas` or `fine-linen` (or `gesso` for a smooth panel);
|
|
39
|
+
`fill_layer` a mid-tone ground (a warm earth or grey) on the bottom layer.
|
|
40
|
+
**Check:** `get_canvas_info` paper; `pick_color` shows the ground.
|
|
41
|
+
2. **Mix the palette.** `mix_colors` for each mixture (read `mixed`), `add_swatch` to keep them;
|
|
42
|
+
`color_harmony` around a key colour if needed.
|
|
43
|
+
3. **Block in thin.** `add_layer` "Block-in"; `oil-flat` or `bristle-flat` with large strokes,
|
|
44
|
+
the big shapes and values only, darks first.
|
|
45
|
+
**Check:** `list_strokes` shows the thin brushes; `render_preview` reads as a simple value
|
|
46
|
+
study.
|
|
47
|
+
4. **Build the forms.** `oil-round` on a new layer, more opaque and more colour; let the brush
|
|
48
|
+
pick up and blend wet paint (`behavior.pickup` 0.3-0.6) for soft turns of form; `blender`
|
|
49
|
+
for transitions.
|
|
50
|
+
5. **Thick paint last.** `add_layer` "Impasto"; `impasto-brush` for ridged strokes,
|
|
51
|
+
`palette-knife` to scrape and smear, `paint-tube` for blobs, in the lights and focal areas.
|
|
52
|
+
**Check:** `get_relief` on that layer has `maxHeight` well above 0.
|
|
53
|
+
6. **Light the relief.** `set_lighting` `azimuth` (degrees clockwise from the top: 315 top-left,
|
|
54
|
+
45 top-right), `elevation` 15-30 to show brushwork, 40-60 for a calmer surface.
|
|
55
|
+
**Check:** `get_relief` `lighting` is what you set; `render_preview` to look.
|
|
56
|
+
7. **Finish.** Details and accents; export. `flatten_relief` only if the user wants the colour
|
|
57
|
+
without texture.
|
|
58
|
+
|
|
59
|
+
## Decision table
|
|
60
|
+
|
|
61
|
+
| Want | Brush or tool |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| Large, thin block-in | `oil-flat`, `bristle-flat` |
|
|
64
|
+
| Soft blended form | `oil-round` with pick-up; `blender` |
|
|
65
|
+
| Visible bristle ridges | `impasto-brush`, `bristle-round` |
|
|
66
|
+
| Scraped, smeared texture | `palette-knife` |
|
|
67
|
+
| Blobs of paint straight from the tube | `paint-tube` |
|
|
68
|
+
| A glossy bead or line | `gloop-pen` |
|
|
69
|
+
| Dry, broken strokes over texture | `dry-brush`, or a lower `impasto.load` |
|
|
70
|
+
| A metallic highlight | `metallic-paint` |
|
|
71
|
+
| Paint that stays exactly as laid | `set_instant_dry` on |
|
|
72
|
+
|
|
73
|
+
## Recipes
|
|
74
|
+
|
|
75
|
+
**Mixed green.** `mix_colors` `{ colors: [{ color: "#1f3a93", weight: 1 }, { color: "#f2c500",
|
|
76
|
+
weight: 1 }] }` → `set_color` with the result's `mixed` → `set_brush` `oil-round` →
|
|
77
|
+
`paint_stroke`.
|
|
78
|
+
|
|
79
|
+
**Impasto highlights.** `add_layer` "Impasto" → `set_brush` `impasto-brush` → `set_color` a
|
|
80
|
+
light → short strokes following the form → `set_lighting` → `get_relief`.
|
|
81
|
+
|
|
82
|
+
## Pitfalls
|
|
83
|
+
|
|
84
|
+
- **Thick before thin.** Heavy paint in the block-in fights every later pass; keep it lean.
|
|
85
|
+
- **Mud from over-mixing.** A brush with high pick-up dragged through many colours greys them;
|
|
86
|
+
`clean_mixer_brush` and fewer passes.
|
|
87
|
+
- **RGB mixing in your head.** Blue and yellow light make grey; paint makes green; use
|
|
88
|
+
`mix_colors`.
|
|
89
|
+
- **Relief everywhere.** Uniform texture flattens the picture; keep shadows thin.
|
|
90
|
+
- **Oil and watercolour on one layer.** A layer keeps the first medium's wet settings.
|
|
91
|
+
- **A lighting change is global**: it lights every thick layer; pick it once, near the end.
|
|
92
|
+
|
|
93
|
+
## Tools used
|
|
94
|
+
|
|
95
|
+
- `get_canvas_info`, `list_layers`, `list_strokes`, `render_preview`, `pick_color`: read and check
|
|
96
|
+
- `set_paper`, `fill_layer`: canvas and a toned ground
|
|
97
|
+
- `add_layer`, `update_layer`: a layer per pass
|
|
98
|
+
- `mix_colors`, `color_harmony`, `add_swatch`, `set_color`: palette
|
|
99
|
+
- `set_brush`, `paint_stroke`, `clean_mixer_brush`: block in, build, blend and impasto strokes
|
|
100
|
+
- `set_lighting`, `get_relief`, `flatten_relief`: the relief and its light
|
|
101
|
+
- `set_instant_dry`: strokes that dry as they end
|
|
102
|
+
- `undo_paint`: take back a step
|
|
103
|
+
- `export_image`: deliver
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Add a thick impasto highlight stroke across the middle of this painting on its own layer, so I can remove it later, and light the paint from the top right.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "paint",
|
|
5
|
+
"width": 64,
|
|
6
|
+
"height": 64,
|
|
7
|
+
"setup": [
|
|
8
|
+
{ "tool": "set_paper", "args": { "preset": "cotton-canvas" } },
|
|
9
|
+
{ "tool": "fill_layer", "args": { "color": "#3b5b7a" } }
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"checks": [
|
|
13
|
+
{ "why": "the highlight is on a new layer above the painting", "tool": "list_layers", "expect": { "layers": { "$length": { "$min": 2 }, "$expr": "v[0].type === 'pixel' && v[0].bounds !== null && v[v.length - 1].bounds !== null" } } },
|
|
14
|
+
{ "why": "thick paint with relief on that layer", "tool": "get_relief", "expect": { "relief": { "maxHeight": { "$min": 50 } } } },
|
|
15
|
+
{ "why": "lit from the top right", "tool": "get_relief", "expect": { "lighting": { "azimuth": { "$min": 20, "$max": 70 } } } }
|
|
16
|
+
],
|
|
17
|
+
"solution": [
|
|
18
|
+
{ "tool": "add_layer", "args": { "name": "Impasto highlights" } },
|
|
19
|
+
{ "tool": "set_brush", "args": { "id": "impasto-brush" } },
|
|
20
|
+
{ "tool": "set_color", "args": { "color": "#f4e7c5" } },
|
|
21
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 8, "y": 32 }, { "x": 56, "y": 30 }] } },
|
|
22
|
+
{ "tool": "set_lighting", "args": { "azimuth": 45 } }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Paint this the traditional way: block in the background first with thin paint, then build the subject in the centre with thick paint on top.",
|
|
3
|
+
"fixture": { "editor": "paint", "width": 64, "height": 64, "setup": [{ "tool": "set_paper", "args": { "preset": "cotton-canvas" } }] },
|
|
4
|
+
"checks": [
|
|
5
|
+
{ "why": "thin paint first", "tool": "list_strokes", "expect": { "strokes": { "$expr": "v.length >= 2 && ['oil-round', 'oil-flat', 'bristle-flat', 'bristle-round', 'dry-brush'].includes(v[0].brushId)" } } },
|
|
6
|
+
{ "why": "thick paint after it", "tool": "list_strokes", "expect": { "strokes": { "$expr": "v.slice(1).some((s) => ['impasto-brush', 'palette-knife', 'paint-tube', 'gloop-pen'].includes(s.brushId))" } } },
|
|
7
|
+
{ "why": "the thick paint stands up from the canvas", "tool": "get_relief", "expect": { "relief": { "maxHeight": { "$min": 50 } } } }
|
|
8
|
+
],
|
|
9
|
+
"solution": [
|
|
10
|
+
{ "tool": "set_brush", "args": { "id": "oil-flat" } },
|
|
11
|
+
{ "tool": "set_color", "args": { "color": "#6b7f6a" } },
|
|
12
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 0, "y": 8 }, { "x": 64, "y": 8 }, { "x": 64, "y": 24 }, { "x": 0, "y": 24 }, { "x": 0, "y": 40 }, { "x": 64, "y": 40 }, { "x": 64, "y": 56 }, { "x": 0, "y": 56 }] } },
|
|
13
|
+
{ "tool": "set_brush", "args": { "id": "impasto-brush" } },
|
|
14
|
+
{ "tool": "set_color", "args": { "color": "#d9783b" } },
|
|
15
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 24, "y": 32 }, { "x": 40, "y": 32 }] } }
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Mix a green from ultramarine (#1f3a93) and cadmium yellow (#f2c500) the way real paint mixes, then paint a short oil stroke with it from (10, 32) to (54, 32).",
|
|
3
|
+
"fixture": { "editor": "paint", "width": 64, "height": 64, "setup": [{ "tool": "set_paper", "args": { "preset": "cotton-canvas" } }] },
|
|
4
|
+
"checks": [
|
|
5
|
+
{ "why": "the stroke's colour is a green", "tool": "list_strokes", "expect": { "strokes": { "$some": { "color": { "$expr": "(([r, g, b]) => g > r && g > b)([1, 3, 5].map((i) => parseInt(v.slice(i, i + 2), 16)))" } } } } },
|
|
6
|
+
{ "why": "the canvas shows green paint", "tool": "pick_color", "args": { "x": 32, "y": 32 }, "expect": { "color": { "$expr": "(([r, g, b]) => g > r && g > b)([1, 3, 5].map((i) => parseInt(v.slice(i, i + 2), 16)))" } } },
|
|
7
|
+
{ "why": "an oil brush", "tool": "list_strokes", "expect": { "strokes": { "$some": { "brushId": { "$in": ["oil-round", "oil-flat", "bristle-round", "bristle-flat", "impasto-brush", "palette-knife"] } } } } }
|
|
8
|
+
],
|
|
9
|
+
"solution": [
|
|
10
|
+
{ "tool": "mix_colors", "args": { "colors": [{ "color": "#1f3a93", "weight": 1 }, { "color": "#f2c500", "weight": 1 }] } },
|
|
11
|
+
{ "tool": "set_brush", "args": { "id": "oil-round" } },
|
|
12
|
+
{ "tool": "set_color", "args": { "color": { "$read": "mix_colors", "args": { "colors": [{ "color": "#1f3a93", "weight": 1 }, { "color": "#f2c500", "weight": 1 }] }, "path": "mixed" } } },
|
|
13
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 10, "y": 32 }, { "x": 54, "y": 32 }] } }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-paint-sketch-to-finish
|
|
3
|
+
description: The sketch-to-finish workflow in the Pixerate paint editor - a loose pencil sketch on its own layer, construction with guides, clean line art inked over it, flat colours on a layer under the lines, shading and light on clipped layers in multiply, shadow or highlight modes, final detail, and a tidy, named layer stack for export. Use when asked to draw and colour, ink a sketch, make line art, add flat colours, shade or light a drawing, take a drawing from sketch to finished illustration, or colour comic or cartoon art, or when add_layer, update_layer, paint_stroke or set_tracing_image are available. Load with pixerate-paint-editor, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-paint-editor
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# From sketch to finished drawing
|
|
10
|
+
|
|
11
|
+
A drawing is finished in passes, each on its own layer, so each can be changed without
|
|
12
|
+
redoing the others: sketch, line art, flat colours, shading and light, details. Layers above
|
|
13
|
+
others can be clipped to them so shading stays inside the flats, and painterly blend modes do
|
|
14
|
+
the glazing. `list_layers` lists the stack top first.
|
|
15
|
+
|
|
16
|
+
This skill is the method. `pixerate-paint-editor` has the full tool reference.
|
|
17
|
+
|
|
18
|
+
## Goal and quality bar
|
|
19
|
+
|
|
20
|
+
- A named layer per pass ("Sketch", "Ink", "Flats", "Shading", "Light"), in the right order:
|
|
21
|
+
flats under the line art, shading and light clipped to the flats.
|
|
22
|
+
- The sketch is hidden (or deleted) in the finished piece.
|
|
23
|
+
- Lines are clean and confident: few long strokes, pressure tapering at the ends.
|
|
24
|
+
- Flats fill the shapes without gaps at the lines; shading and light stay inside them.
|
|
25
|
+
|
|
26
|
+
## Inputs to confirm
|
|
27
|
+
|
|
28
|
+
- **Style:** line and flat colour (comic, cartoon), or soft rendering.
|
|
29
|
+
- **Line weight and colour** (black ink, or a coloured line).
|
|
30
|
+
- **Palette** for the flats; light direction for the shading.
|
|
31
|
+
|
|
32
|
+
## Working method
|
|
33
|
+
|
|
34
|
+
1. **Ground.** `set_paper` `smooth` (bristol) for ink and digital-looking work, `cartridge` for
|
|
35
|
+
pencil; `get_canvas_info` to confirm.
|
|
36
|
+
2. **Sketch.** Rename or add a layer "Sketch" (`update_layer` `name`, or `add_layer`); a light
|
|
37
|
+
pencil (`pencil-hb`) in a mid grey or blue, loose construction lines. `set_symmetry` for
|
|
38
|
+
symmetric subjects, `set_perspective_guide` for buildings, `set_tracing_image` to trace a
|
|
39
|
+
reference.
|
|
40
|
+
**Check:** `list_strokes` shows pencil strokes on the Sketch layer.
|
|
41
|
+
3. **Ink.** `add_layer` "Ink" above it; `ink-pen` or `brush-pen`, dark colour, long strokes with
|
|
42
|
+
points that carry `pressure` (lighter at the ends) and `t` for speed dynamics.
|
|
43
|
+
**Check:** `pick_color` on a line is dark.
|
|
44
|
+
4. **Hide the sketch.** `update_layer` the Sketch layer `visible: false` (or lower its `opacity`
|
|
45
|
+
while inking, then hide it).
|
|
46
|
+
**Check:** `list_layers` shows it hidden.
|
|
47
|
+
5. **Flats.** `add_layer` "Flats", then `move_layer` it below the Ink layer; fill each shape with
|
|
48
|
+
its base colour (`marker` or a flat brush, or `fill_layer` for a background).
|
|
49
|
+
**Check:** `pick_color` inside a shape is the flat colour; on a line still the ink.
|
|
50
|
+
6. **Shading and light.** `add_layer` "Shading" directly above the flats, `update_layer`
|
|
51
|
+
`blend: "multiply"` (or `shadow`) and `clipped: true`; paint shadows on the side away from the
|
|
52
|
+
light. A "Light" layer the same way with `highlight` or `screen`.
|
|
53
|
+
**Check:** `list_layers`: the clipped layers sit directly above the flats.
|
|
54
|
+
7. **Details and finish.** Accents, textures and line fixes on their own layers; `render_preview`;
|
|
55
|
+
`export_image`, or `export_psd` to keep the layers (painterly modes export as the nearest
|
|
56
|
+
standard mode, with a warning).
|
|
57
|
+
|
|
58
|
+
## Decision table
|
|
59
|
+
|
|
60
|
+
| Pass | Brush and layer |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| Sketch | `pencil-hb`, light colour, own layer, hidden at the end |
|
|
63
|
+
| Line art | `ink-pen` (even), `brush-pen` (swelling); own layer on top |
|
|
64
|
+
| Flats | `marker` or a flat brush on a layer under the ink |
|
|
65
|
+
| Shadows | Clipped layer, `multiply` or `shadow`, a cool colour |
|
|
66
|
+
| Light, glow | Clipped layer, `highlight` or `screen` |
|
|
67
|
+
| Warm or cool a whole area | Clipped layer in `tint` |
|
|
68
|
+
| Repeated marks (hatching, grass) | One stroke, then `replay_strokes` with offsets |
|
|
69
|
+
|
|
70
|
+
## Recipes
|
|
71
|
+
|
|
72
|
+
**Flats under ink.** `add_layer` "Flats" → `move_layer` with the new layer's id and `index` below
|
|
73
|
+
the Ink layer → `set_brush` `marker` → `set_color` → strokes inside the shape.
|
|
74
|
+
|
|
75
|
+
**Clipped shading.** `add_layer` "Shading" above Flats → `update_layer` `{ blend: "multiply",
|
|
76
|
+
clipped: true }` → `set_brush` `airbrush` → paint the shadow side.
|
|
77
|
+
|
|
78
|
+
## Pitfalls
|
|
79
|
+
|
|
80
|
+
- **Inking on the sketch layer** mixes the two; you cannot hide the sketch afterwards.
|
|
81
|
+
- **Flats above the ink** cover the lines; they belong under them.
|
|
82
|
+
- **Unclipped shading** spills outside the shapes; clip it to the flats.
|
|
83
|
+
- **Shadows in black** look dirty; use a cool, darker version of the colour in multiply or
|
|
84
|
+
`shadow`.
|
|
85
|
+
- **Many short, hairy strokes** make weak line art; fewer, longer strokes with pressure.
|
|
86
|
+
- **`list_layers` is top first**: index 0 is the top layer; `move_layer` `index` counts from the
|
|
87
|
+
bottom.
|
|
88
|
+
|
|
89
|
+
## Tools used
|
|
90
|
+
|
|
91
|
+
- `get_canvas_info`, `list_layers`, `list_strokes`, `pick_color`, `render_preview`: read and check
|
|
92
|
+
- `set_paper`, `fill_layer`: the ground and backgrounds
|
|
93
|
+
- `add_layer`, `update_layer`, `move_layer`, `remove_layer`: one layer per pass; hide, clip, blend
|
|
94
|
+
- `set_brush`, `set_color`, `paint_stroke`, `replay_strokes`: sketch, ink, flats and shading
|
|
95
|
+
- `set_symmetry`, `set_perspective_guide`, `set_tracing_image`: construction guides
|
|
96
|
+
- `undo_paint`: take back a step
|
|
97
|
+
- `export_image`, `export_psd`: deliver
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Add shading to the yellow shape on a separate layer that only affects the yellow and darkens it (multiply), so I can adjust or remove it later.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "paint",
|
|
5
|
+
"width": 64,
|
|
6
|
+
"height": 64,
|
|
7
|
+
"setup": [
|
|
8
|
+
{ "tool": "set_paper", "args": { "preset": "smooth" } },
|
|
9
|
+
{ "tool": "update_layer", "args": { "layerId": { "$read": "list_layers", "path": "layers.0.id" }, "name": "Flats" } },
|
|
10
|
+
{ "tool": "set_brush", "args": { "id": "marker" } },
|
|
11
|
+
{ "tool": "set_color", "args": { "color": "#f2c14e" } },
|
|
12
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 16, "y": 20 }, { "x": 48, "y": 20 }, { "x": 48, "y": 32 }, { "x": 16, "y": 32 }, { "x": 16, "y": 44 }, { "x": 48, "y": 44 }] } }
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"checks": [
|
|
16
|
+
{ "why": "a multiply layer clipped to the layer below", "tool": "list_layers", "expect": { "layers": { "$some": { "blend": "multiply", "clipped": true } } } },
|
|
17
|
+
{ "why": "directly above the flats", "tool": "list_layers", "expect": { "layers": { "$expr": "(() => { const i = v.findIndex((l) => l.name === 'Flats'); return i > 0 && v[i - 1].clipped && v[i - 1].bounds !== null; })()" } } }
|
|
18
|
+
],
|
|
19
|
+
"solution": [
|
|
20
|
+
{ "tool": "add_layer", "args": { "name": "Shading" } },
|
|
21
|
+
{ "tool": "update_layer", "args": { "layerId": { "$read": "list_layers", "path": "layers.0.id" }, "blend": "multiply", "clipped": true } },
|
|
22
|
+
{ "tool": "set_brush", "args": { "id": "airbrush" } },
|
|
23
|
+
{ "tool": "set_color", "args": { "color": "#b08a6a" } },
|
|
24
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 40, "y": 22 }, { "x": 46, "y": 42 }] } }
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Colour the inside of the inked box with a flat warm yellow, on its own layer under the line art so the lines stay on top.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "paint",
|
|
5
|
+
"width": 64,
|
|
6
|
+
"height": 64,
|
|
7
|
+
"setup": [
|
|
8
|
+
{ "tool": "set_paper", "args": { "preset": "smooth" } },
|
|
9
|
+
{ "tool": "update_layer", "args": { "layerId": { "$read": "list_layers", "path": "layers.0.id" }, "name": "Ink" } },
|
|
10
|
+
{ "tool": "set_brush", "args": { "id": "ink-pen" } },
|
|
11
|
+
{ "tool": "set_color", "args": { "color": "#111111" } },
|
|
12
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 12, "y": 12 }, { "x": 52, "y": 12 }, { "x": 52, "y": 52 }, { "x": 12, "y": 52 }, { "x": 12, "y": 12 }] } }
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"checks": [
|
|
16
|
+
{ "why": "a colour layer below the Ink layer (list_layers is top first)", "tool": "list_layers", "expect": { "layers": { "$expr": "(() => { const i = v.findIndex((l) => l.name === 'Ink'); return i >= 0 && v.slice(i + 1).some((l) => l.type === 'pixel' && l.bounds); })()" } } },
|
|
17
|
+
{ "why": "the inside is yellow", "tool": "pick_color", "args": { "x": 32, "y": 32 }, "expect": { "color": { "$expr": "(([r, g, b]) => r > 200 && g > 160 && b < r - 60)([1, 3, 5].map((i) => parseInt(v.slice(i, i + 2), 16)))" } } },
|
|
18
|
+
{ "why": "the line is still dark on top", "tool": "pick_color", "args": { "x": 32, "y": 12 }, "expect": { "color": { "$expr": "(([r, g, b]) => r + g + b < 300)([1, 3, 5].map((i) => parseInt(v.slice(i, i + 2), 16)))" } } }
|
|
19
|
+
],
|
|
20
|
+
"solution": [
|
|
21
|
+
{ "tool": "add_layer", "args": { "name": "Flats" } },
|
|
22
|
+
{ "tool": "move_layer", "args": { "layerId": { "$read": "list_layers", "path": "layers.0.id" }, "index": 0 } },
|
|
23
|
+
{ "tool": "set_brush", "args": { "id": "marker" } },
|
|
24
|
+
{ "tool": "set_color", "args": { "color": "#f2c14e" } },
|
|
25
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 14, "y": 16 }, { "x": 50, "y": 16 }, { "x": 50, "y": 24 }, { "x": 14, "y": 24 }, { "x": 14, "y": 32 }, { "x": 50, "y": 32 }, { "x": 50, "y": 40 }, { "x": 14, "y": 40 }, { "x": 14, "y": 48 }, { "x": 50, "y": 48 }] } }
|
|
26
|
+
]
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Start a drawing of a box: a light pencil sketch first, then clean ink lines over it on a separate layer, and hide the sketch when the inking is done.",
|
|
3
|
+
"fixture": { "editor": "paint", "width": 64, "height": 64, "setup": [{ "tool": "set_paper", "args": { "preset": "smooth" } }] },
|
|
4
|
+
"checks": [
|
|
5
|
+
{ "why": "sketch and ink on separate layers", "tool": "list_strokes", "expect": { "strokes": { "$expr": "v.some((s) => s.brushId.startsWith('pencil')) && v.some((s) => ['ink-pen', 'brush-pen'].includes(s.brushId)) && new Set(v.map((s) => s.layerId)).size >= 2" } } },
|
|
6
|
+
{ "why": "the sketch layer is hidden, the ink layer shown", "tool": "list_layers", "expect": { "layers": { "$some": { "visible": false }, "$length": { "$min": 2 } } } },
|
|
7
|
+
{ "why": "the ink is visible on the page", "tool": "pick_color", "args": { "x": 32, "y": 12 }, "expect": { "color": { "$expr": "(([r, g, b]) => r + g + b < 300)([1, 3, 5].map((i) => parseInt(v.slice(i, i + 2), 16)))" } } }
|
|
8
|
+
],
|
|
9
|
+
"solution": [
|
|
10
|
+
{ "tool": "update_layer", "args": { "layerId": { "$read": "list_layers", "path": "layers.0.id" }, "name": "Sketch" } },
|
|
11
|
+
{ "tool": "set_brush", "args": { "id": "pencil-hb" } },
|
|
12
|
+
{ "tool": "set_color", "args": { "color": "#8a8f98" } },
|
|
13
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 12, "y": 12 }, { "x": 52, "y": 12 }, { "x": 52, "y": 52 }, { "x": 12, "y": 52 }, { "x": 12, "y": 12 }] } },
|
|
14
|
+
{ "tool": "add_layer", "args": { "name": "Ink" } },
|
|
15
|
+
{ "tool": "set_brush", "args": { "id": "ink-pen" } },
|
|
16
|
+
{ "tool": "set_color", "args": { "color": "#111111" } },
|
|
17
|
+
{ "tool": "paint_stroke", "args": { "points": [{ "x": 12, "y": 12 }, { "x": 52, "y": 12 }, { "x": 52, "y": 52 }, { "x": 12, "y": 52 }, { "x": 12, "y": 12 }] } },
|
|
18
|
+
{ "tool": "update_layer", "args": { "layerId": { "$read": "list_layers", "path": "layers.1.id" }, "visible": false } }
|
|
19
|
+
]
|
|
20
|
+
}
|