@pixerate/editor 0.221.1 → 0.222.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.
- package/dist/image-editor/edit-tools.d.cts +1 -1
- package/dist/image-editor/edit-tools.d.ts +1 -1
- package/dist/image-editor/index.cjs +35 -2
- package/dist/image-editor/index.js +35 -2
- package/package.json +1 -1
- package/skills/README.md +10 -3
- package/skills/pixerate-image-editor/SKILL.md +25 -76
- package/skills/pixerate-image-editor/references/free-transform.md +11 -0
- package/skills/pixerate-image-editor/references/layer-effects.md +12 -0
- package/skills/pixerate-image-editor/references/smart-objects.md +9 -0
- package/skills/pixerate-image-photo-correction/SKILL.md +129 -0
- package/skills/pixerate-image-photo-correction/evals/crop-for-instagram.json +19 -0
- package/skills/pixerate-image-photo-correction/evals/lift-shadows-keep-sky.json +20 -0
- package/skills/pixerate-image-photo-correction/evals/neutralise-cast.json +25 -0
- package/skills/pixerate-image-portrait-retouch/SKILL.md +115 -0
- package/skills/pixerate-image-portrait-retouch/evals/brighten-eyes.json +25 -0
- package/skills/pixerate-image-portrait-retouch/evals/remove-blemishes.json +23 -0
- package/skills/pixerate-image-portrait-retouch/evals/remove-stray-hair.json +18 -0
- package/skills/pixerate-image-product-photo/SKILL.md +126 -0
- package/skills/pixerate-image-product-photo/evals/grounding-shadow.json +19 -0
- package/skills/pixerate-image-product-photo/evals/square-crop.json +19 -0
- package/skills/pixerate-image-product-photo/evals/white-background.json +23 -0
- package/skills/pixerate-vector/SKILL.md +11 -61
- package/skills/pixerate-vector/references/best-practices.md +62 -0
- package/skills/pixerate-vector-icon-set/SKILL.md +111 -0
- package/skills/pixerate-vector-icon-set/evals/consistent-strokes.json +22 -0
- package/skills/pixerate-vector-icon-set/evals/plus-icon.json +15 -0
- package/skills/pixerate-vector-icon-set/evals/repeats-as-symbol.json +192 -0
- package/skills/pixerate-vector-illustration/SKILL.md +112 -0
- package/skills/pixerate-vector-illustration/evals/clip-stripes.json +21 -0
- package/skills/pixerate-vector-illustration/evals/radial-sun.json +16 -0
- package/skills/pixerate-vector-illustration/evals/subtle-shadow.json +16 -0
- package/skills/pixerate-vector-logo/SKILL.md +107 -0
- package/skills/pixerate-vector-logo/evals/centre-lockup.json +20 -0
- package/skills/pixerate-vector-logo/evals/knockout-ring.json +21 -0
- package/skills/pixerate-vector-logo/evals/live-union.json +19 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-vector-icon-set
|
|
3
|
+
description: The icon set workflow in the Pixerate vector editor - drawing line or solid icons on a consistent grid (24, 32 or 48 units) with padding and keylines, one stroke width, cap and join style and corner radius across the set, anchors on whole or half units so they render crisp, clean minimal paths, repeats as symbols, and checking and exporting each icon as SVG. Use when asked to draw, design, redraw, clean up or make consistent an icon or a set of icons, glyphs or pictograms, or to export icons as SVG, or when vector.addPath, vector.setStyle, vector.makeSymbol or vector.exportSvg are available. Load with pixerate-vector, which lists every command.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-vector
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Drawing an icon set
|
|
10
|
+
|
|
11
|
+
An icon set reads as one family when every icon shares the same few decisions: the grid, the
|
|
12
|
+
padding, the stroke width, the cap and join, the corner radius and the visual weight. Make
|
|
13
|
+
those decisions once, write them down, and apply them to every icon; then check each icon
|
|
14
|
+
against them in numbers (`vector.list` bounds, stroke widths and `d`) before looking.
|
|
15
|
+
|
|
16
|
+
This skill is the method. `pixerate-vector` has the full command reference; run the commands
|
|
17
|
+
with `run_command`, or several as one undo step with `run_commands` and `atomic: true`.
|
|
18
|
+
|
|
19
|
+
## Goal and quality bar
|
|
20
|
+
|
|
21
|
+
- One grid: a square artboard of 24 (UI), 32 or 48 units, the same for the whole set.
|
|
22
|
+
- Padding kept: nothing, stroke included, closer than 2 units to the edge on a 24 grid (1/12 of
|
|
23
|
+
the side on others). Strokes count half their width outside the path.
|
|
24
|
+
- One stroke width (2 on 24 is the common default; 1.5 for a lighter set), one cap and join
|
|
25
|
+
style (round for friendly sets, butt and miter for geometric ones), one corner radius.
|
|
26
|
+
- Crisp: with an even stroke width (2) put the path's anchors on whole units, with an odd one
|
|
27
|
+
(1, 3) on half units, so the stroke's edges fall on pixel boundaries at 1×.
|
|
28
|
+
- Clean paths: the fewest anchors that make the shape, no stray or zero-length segments, no
|
|
29
|
+
hidden overlapping shapes.
|
|
30
|
+
- Visually balanced: icons look the same size (circles a little larger than squares), and are
|
|
31
|
+
optically centred.
|
|
32
|
+
|
|
33
|
+
## Inputs to confirm
|
|
34
|
+
|
|
35
|
+
- **Grid and stroke:** the size (24 by default) and stroke width (2 by default), or an existing
|
|
36
|
+
icon to match (read it first).
|
|
37
|
+
- **Style:** outline (stroked) or solid (filled), and rounded or sharp.
|
|
38
|
+
- **Output:** one SVG per icon, a sprite, or symbols in one document.
|
|
39
|
+
|
|
40
|
+
## Working method
|
|
41
|
+
|
|
42
|
+
1. **Read what exists.** `vector.list`: the artboard size and any icons already drawn, with their
|
|
43
|
+
`strokeWidth`, `bounds` and `d`; `vector.getAppearance` for caps, joins and alignment.
|
|
44
|
+
**Check:** you can state the set's grid, padding, stroke, caps, joins and radius.
|
|
45
|
+
2. **Draw on the grid.** Prefer `vector.addShape` primitives (`rect` with `cornerRadius`,
|
|
46
|
+
`ellipse`, `line`, `polygon`) and `vector.addPath` with whole or half-unit coordinates; give
|
|
47
|
+
`fill: null`, `stroke`, `strokeWidth`, `strokeLinecap` and `strokeLinejoin` in the same call.
|
|
48
|
+
Batch one icon's parts in one `run_commands` with `atomic: true` and a `label`.
|
|
49
|
+
**Check:** `vector.list` bounds plus half the stroke stay inside the padding.
|
|
50
|
+
3. **Combine.** Join parts that should be one shape with `vector.boolean` (`union`; `subtract`
|
|
51
|
+
for cut-outs); use `vector.outlineStroke` only for a solid set or when the export target needs
|
|
52
|
+
fills. Then `vector.removeRedundantPoints`.
|
|
53
|
+
**Check:** `d` has no more anchors than the shape needs; coordinates are still on the grid.
|
|
54
|
+
4. **Match the set.** `vector.setStyle` on all the set's paths at once (`ids` from `vector.list`)
|
|
55
|
+
for stroke width, caps and joins; `vector.setCorners` for one radius.
|
|
56
|
+
**Check:** every path's `strokeWidth` is the set's; caps and joins in `vector.getAppearance`.
|
|
57
|
+
5. **Balance.** Compare the icons' bounds: a circle about 10 % larger than a square of the same
|
|
58
|
+
weight looks the same size; centre shapes optically (a play triangle shifts right).
|
|
59
|
+
**Check:** each icon's bounds centre is within half a unit of the artboard centre (or its
|
|
60
|
+
intended optical centre).
|
|
61
|
+
6. **Repeats become symbols.** The same element used several times (a star in a rating, a
|
|
62
|
+
chevron) is `vector.makeSymbol` once and `vector.placeSymbol` elsewhere.
|
|
63
|
+
7. **Export.** `vector.exportSvg` (or `vector.exportFrameSvg` per artboard) with `precision` 2;
|
|
64
|
+
read the markup: no transforms where plain coordinates would do, no hidden nodes.
|
|
65
|
+
|
|
66
|
+
## Decision table
|
|
67
|
+
|
|
68
|
+
| Need | Approach |
|
|
69
|
+
| --- | --- |
|
|
70
|
+
| Outline icon | Stroked paths, `fill: null`, round caps and joins |
|
|
71
|
+
| Solid icon | Filled shapes; `vector.outlineStroke` turns a stroked sketch into fills |
|
|
72
|
+
| A rounded rectangle or badge | `vector.addShape` `rect` with `cornerRadius`, not drawn arcs |
|
|
73
|
+
| A cut-out (a hole, a notch) | `vector.boolean` `subtract`, or `fillRule: "evenodd"` |
|
|
74
|
+
| Several icons on one page | One frame per icon (`vector.addFrame`), exported with `vector.exportFrameSvg` |
|
|
75
|
+
| One element repeated | `vector.makeSymbol` and `vector.placeSymbol` |
|
|
76
|
+
| Odd stroke width (1, 3) | Put anchors on half units so the edges land on pixels |
|
|
77
|
+
|
|
78
|
+
## Recipes
|
|
79
|
+
|
|
80
|
+
**A line icon.** One `vector.addPath` with `d` on the grid (`M12 5L12 19M5 12L19 12` is a plus
|
|
81
|
+
on 24), `fill: null`, `stroke`, `strokeWidth: 2`, `strokeLinecap: "round"`,
|
|
82
|
+
`strokeLinejoin: "round"`; then `vector.list` to check bounds against the padding.
|
|
83
|
+
|
|
84
|
+
**Make a set consistent.** `vector.list` → `vector.setStyle` with every path's id, the set's
|
|
85
|
+
`strokeWidth`, `strokeLinecap` and `strokeLinejoin` in one call (one undo step) → `vector.list`
|
|
86
|
+
to confirm nothing moved.
|
|
87
|
+
|
|
88
|
+
**Copies into a symbol.** `vector.makeSymbol` on the first copy (it is replaced by an
|
|
89
|
+
instance), `vector.delete` the other copies, `vector.placeSymbol` at their centres.
|
|
90
|
+
|
|
91
|
+
## Pitfalls
|
|
92
|
+
|
|
93
|
+
- **Bounds without the stroke.** `vector.list` bounds are the path's geometry; add half the
|
|
94
|
+
stroke width on each side when checking padding.
|
|
95
|
+
- **Off-grid anchors** after a scale or rotate blur at 1×; check `d` and round coordinates.
|
|
96
|
+
- **Default styles.** `vector.addShape` adds a grey fill and a dark 2-unit stroke unless you pass
|
|
97
|
+
`fill` and `stroke`; set them in the same call.
|
|
98
|
+
- **Scaling strokes.** Scaling an icon scales its geometry, not its stroke width; reset
|
|
99
|
+
`strokeWidth` afterwards so the set stays consistent.
|
|
100
|
+
- **Too many anchors** from booleans or traced input; `vector.removeRedundantPoints`.
|
|
101
|
+
- **Mixed caps and joins** across a set are easy to miss by eye; read `vector.getAppearance`.
|
|
102
|
+
|
|
103
|
+
## Tools used
|
|
104
|
+
|
|
105
|
+
- `vector.list`, `vector.getAppearance`: bounds, stroke widths, `d`, caps and joins
|
|
106
|
+
- `vector.addShape`, `vector.addPath`: draw on the grid
|
|
107
|
+
- `vector.setStyle`, `vector.setCorners`: one stroke, cap, join and radius across the set
|
|
108
|
+
- `vector.boolean`, `vector.outlineStroke`, `vector.removeRedundantPoints`: combine and clean paths
|
|
109
|
+
- `vector.transform`, `vector.align`: position and centre
|
|
110
|
+
- `vector.makeSymbol`, `vector.placeSymbol`, `vector.delete`: repeats as symbols
|
|
111
|
+
- `vector.addFrame`, `vector.exportFrameSvg`, `vector.exportSvg`: one artboard per icon, export
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "These three icons are from one set but their stroke widths don't match (1, 2 and 3 units). Make them all 2 units with round caps and joins, without moving or resizing them.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 72,
|
|
6
|
+
"height": 24,
|
|
7
|
+
"setup": [
|
|
8
|
+
{ "id": "vector.addPath", "params": { "d": "M4 12L20 12", "name": "Minus", "fill": null, "stroke": "#000", "strokeWidth": 1 } },
|
|
9
|
+
{ "id": "vector.addPath", "params": { "d": "M28 6L44 18M44 6L28 18", "name": "Close", "fill": null, "stroke": "#000", "strokeWidth": 2 } },
|
|
10
|
+
{ "id": "vector.addPath", "params": { "d": "M52 12L58 18L68 6", "name": "Check", "fill": null, "stroke": "#000", "strokeWidth": 3 } }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"checks": [
|
|
14
|
+
{ "why": "every icon at 2 units", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$length": 3, "$every": { "strokeWidth": 2 } } } } },
|
|
15
|
+
{ "why": "the icons have not moved or changed size", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": [{ "name": "Minus", "bounds": { "x": 4, "y": 12, "width": 16, "height": 0 } }, { "name": "Close", "bounds": { "x": 28, "y": 6, "width": 16, "height": 12 } }, { "name": "Check", "bounds": { "x": 52, "y": 6, "width": 16, "height": 12 } }] } } },
|
|
16
|
+
{ "why": "round caps on the minus too", "tool": "run_command", "args": { "id": "vector.getAppearance", "params": { "id": { "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.0.id" } } }, "expect": { "result": { "strokes": { "$every": { "strokeLinecap": "round", "strokeLinejoin": "round" } } } } }
|
|
17
|
+
],
|
|
18
|
+
"solution": [
|
|
19
|
+
{ "tool": "run_command", "args": { "id": "vector.list" } },
|
|
20
|
+
{ "tool": "run_command", "args": { "id": "vector.setStyle", "params": { "ids": { "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.*.id" }, "strokeWidth": 2, "strokeLinecap": "round", "strokeLinejoin": "round" } } }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Draw a 'plus' line icon on this 24-unit icon grid: 2-unit strokes with round caps, no fill, centred, keeping a 2-unit padding around it.",
|
|
3
|
+
"fixture": { "editor": "vector", "width": 24, "height": 24 },
|
|
4
|
+
"checks": [
|
|
5
|
+
{ "why": "a stroked, unfilled path at the set's stroke width", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$some": { "type": "path", "fill": null, "strokeWidth": 2 } } } } },
|
|
6
|
+
{ "why": "every stroke stays inside the 2-unit padding", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$length": { "$min": 1 }, "$every": { "$expr": "!v.bounds || (v.bounds.x - (v.strokeWidth || 0) / 2 >= 1.99 && v.bounds.y - (v.strokeWidth || 0) / 2 >= 1.99 && v.bounds.x + v.bounds.width + (v.strokeWidth || 0) / 2 <= 22.01 && v.bounds.y + v.bounds.height + (v.strokeWidth || 0) / 2 <= 22.01)" } } } } },
|
|
7
|
+
{ "why": "centred on the grid", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$some": { "type": "path", "bounds": { "$expr": "Math.abs(v.x + v.width / 2 - 12) <= 0.5 && Math.abs(v.y + v.height / 2 - 12) <= 0.5 && Math.abs(v.width - v.height) <= 0.5" } } } } } },
|
|
8
|
+
{ "why": "anchors on whole or half units, so strokes render crisp", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$every": { "$expr": "!v.d || (v.d.match(/-?\\d*\\.?\\d+/g) || []).every((n) => Math.abs(n * 2 - Math.round(n * 2)) < 1e-6)" } } } } },
|
|
9
|
+
{ "why": "round caps", "tool": "run_command", "args": { "id": "vector.getAppearance", "params": { "id": { "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.0.id" } } }, "expect": { "result": { "strokes": { "$some": { "strokeLinecap": "round" } } } } }
|
|
10
|
+
],
|
|
11
|
+
"solution": [
|
|
12
|
+
{ "tool": "run_command", "args": { "id": "vector.addPath", "params": { "d": "M12 5L12 19M5 12L19 12", "name": "Plus", "fill": null, "stroke": "#000000", "strokeWidth": 2, "strokeLinecap": "round", "strokeLinejoin": "round" } } },
|
|
13
|
+
{ "tool": "run_command", "args": { "id": "vector.list" } }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "The three stars are copies of the same rating icon. Make them one reusable symbol, so that editing it once updates all three, and keep them where they are.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 72,
|
|
6
|
+
"height": 24,
|
|
7
|
+
"setup": [
|
|
8
|
+
{
|
|
9
|
+
"id": "vector.addShape",
|
|
10
|
+
"params": {
|
|
11
|
+
"kind": "star",
|
|
12
|
+
"x": 2,
|
|
13
|
+
"y": 2,
|
|
14
|
+
"width": 20,
|
|
15
|
+
"height": 20,
|
|
16
|
+
"name": "Star",
|
|
17
|
+
"fill": "#f5b301",
|
|
18
|
+
"stroke": null
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "vector.addShape",
|
|
23
|
+
"params": {
|
|
24
|
+
"kind": "star",
|
|
25
|
+
"x": 26,
|
|
26
|
+
"y": 2,
|
|
27
|
+
"width": 20,
|
|
28
|
+
"height": 20,
|
|
29
|
+
"name": "Star",
|
|
30
|
+
"fill": "#f5b301",
|
|
31
|
+
"stroke": null
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "vector.addShape",
|
|
36
|
+
"params": {
|
|
37
|
+
"kind": "star",
|
|
38
|
+
"x": 50,
|
|
39
|
+
"y": 2,
|
|
40
|
+
"width": 20,
|
|
41
|
+
"height": 20,
|
|
42
|
+
"name": "Star",
|
|
43
|
+
"fill": "#f5b301",
|
|
44
|
+
"stroke": null
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"checks": [
|
|
50
|
+
{
|
|
51
|
+
"why": "one symbol",
|
|
52
|
+
"tool": "run_command",
|
|
53
|
+
"args": {
|
|
54
|
+
"id": "vector.listSymbols"
|
|
55
|
+
},
|
|
56
|
+
"expect": {
|
|
57
|
+
"result": {
|
|
58
|
+
"symbols": {
|
|
59
|
+
"$length": 1
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"why": "three instances of it and no loose copies",
|
|
66
|
+
"tool": "run_command",
|
|
67
|
+
"args": {
|
|
68
|
+
"id": "vector.list"
|
|
69
|
+
},
|
|
70
|
+
"expect": {
|
|
71
|
+
"result": {
|
|
72
|
+
"nodes": {
|
|
73
|
+
"$length": 3,
|
|
74
|
+
"$every": {
|
|
75
|
+
"type": "instance"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"why": "in their original places",
|
|
83
|
+
"tool": "run_command",
|
|
84
|
+
"args": {
|
|
85
|
+
"id": "vector.list"
|
|
86
|
+
},
|
|
87
|
+
"expect": {
|
|
88
|
+
"result": {
|
|
89
|
+
"nodes": {
|
|
90
|
+
"$expr": "[2, 26, 50].every((x) => v.some((n) => Math.abs(n.bounds.x + n.bounds.width / 2 - (x + 10)) <= 0.5))"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"solution": [
|
|
97
|
+
{
|
|
98
|
+
"tool": "run_command",
|
|
99
|
+
"args": {
|
|
100
|
+
"id": "vector.list"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"tool": "run_command",
|
|
105
|
+
"args": {
|
|
106
|
+
"id": "vector.makeSymbol",
|
|
107
|
+
"params": {
|
|
108
|
+
"ids": [
|
|
109
|
+
{
|
|
110
|
+
"$read": "run_command",
|
|
111
|
+
"args": {
|
|
112
|
+
"id": "vector.list"
|
|
113
|
+
},
|
|
114
|
+
"path": "result.nodes.0.id"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"name": "Rating star"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"tool": "run_command",
|
|
123
|
+
"args": {
|
|
124
|
+
"id": "vector.delete",
|
|
125
|
+
"params": {
|
|
126
|
+
"ids": [
|
|
127
|
+
{
|
|
128
|
+
"$read": "run_command",
|
|
129
|
+
"args": {
|
|
130
|
+
"id": "vector.list"
|
|
131
|
+
},
|
|
132
|
+
"path": "result.nodes.1.id"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"$read": "run_command",
|
|
136
|
+
"args": {
|
|
137
|
+
"id": "vector.list"
|
|
138
|
+
},
|
|
139
|
+
"path": "result.nodes.2.id"
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"tool": "run_command",
|
|
147
|
+
"args": {
|
|
148
|
+
"id": "vector.placeSymbol",
|
|
149
|
+
"params": {
|
|
150
|
+
"symbolId": {
|
|
151
|
+
"$read": "run_command",
|
|
152
|
+
"args": {
|
|
153
|
+
"id": "vector.listSymbols"
|
|
154
|
+
},
|
|
155
|
+
"path": "result.symbols.0.id"
|
|
156
|
+
},
|
|
157
|
+
"x": 36,
|
|
158
|
+
"y": {
|
|
159
|
+
"$read": "run_command",
|
|
160
|
+
"args": {
|
|
161
|
+
"id": "vector.list"
|
|
162
|
+
},
|
|
163
|
+
"path": "result.nodes.0.transform.5"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"tool": "run_command",
|
|
170
|
+
"args": {
|
|
171
|
+
"id": "vector.placeSymbol",
|
|
172
|
+
"params": {
|
|
173
|
+
"symbolId": {
|
|
174
|
+
"$read": "run_command",
|
|
175
|
+
"args": {
|
|
176
|
+
"id": "vector.listSymbols"
|
|
177
|
+
},
|
|
178
|
+
"path": "result.symbols.0.id"
|
|
179
|
+
},
|
|
180
|
+
"x": 60,
|
|
181
|
+
"y": {
|
|
182
|
+
"$read": "run_command",
|
|
183
|
+
"args": {
|
|
184
|
+
"id": "vector.list"
|
|
185
|
+
},
|
|
186
|
+
"path": "result.nodes.0.transform.5"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-vector-illustration
|
|
3
|
+
description: The illustration workflow in the Pixerate vector editor - blocking in shapes back to front, grouping and naming parts, colour from a small palette, gradients (linear, radial, mesh and freeform) with even interpolation, stacked fills and strokes instead of duplicate shapes, clipping and opacity masks, blends, subtle shadows and glows with one light direction, and preparing artboards for export. Use when asked to draw, illustrate or paint a scene, character, object, spot illustration, poster or background in vector, add shading, gradients, highlights, shadows or texture, show art inside a shape, or when vector.setGradient, vector.addFill, vector.makeClippingMask, vector.addFilter or vector.blend are available. Load with pixerate-vector, which lists every command.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-vector
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Making an illustration
|
|
10
|
+
|
|
11
|
+
An illustration is built like a painting: big shapes first, back to front, then form (light and
|
|
12
|
+
shade), then detail, then finish. In vector, every stage should stay editable: shading lives in
|
|
13
|
+
gradients and stacked fills rather than extra shapes, art shown inside a shape lives in masks,
|
|
14
|
+
and effects stay live until the file is delivered.
|
|
15
|
+
|
|
16
|
+
This skill is the method. `pixerate-vector` has the full command reference; run the commands
|
|
17
|
+
with `run_command`, or several as one undo step with `run_commands` and `atomic: true`.
|
|
18
|
+
|
|
19
|
+
## Goal and quality bar
|
|
20
|
+
|
|
21
|
+
- A clear structure: parts grouped and named ("Sky", "Hill far", "Tree 1"), stacked back to
|
|
22
|
+
front, the tree shallow.
|
|
23
|
+
- A small palette (5-8 colours plus tints), used consistently; gradients two to four stops.
|
|
24
|
+
- One light direction for every highlight, shade and shadow.
|
|
25
|
+
- Non-destructive: gradients, appearance stacks, masks, blends and filters are live; shapes are
|
|
26
|
+
not duplicated to fake shading.
|
|
27
|
+
- Shadows and glows subtle: drop shadows black at 0.15-0.3 opacity, offset 2-8 units straight
|
|
28
|
+
away from the light, blur about twice the offset.
|
|
29
|
+
|
|
30
|
+
## Inputs to confirm
|
|
31
|
+
|
|
32
|
+
- **Subject and format:** what to draw, the artboard size or ratio.
|
|
33
|
+
- **Style:** flat (solid fills), soft (gradients), or textured; outlined or not.
|
|
34
|
+
- **Palette:** brand colours or free; the light direction if it matters.
|
|
35
|
+
|
|
36
|
+
## Working method
|
|
37
|
+
|
|
38
|
+
1. **Read and plan.** `vector.list` for the artboard and what exists. Plan the layers from back to
|
|
39
|
+
front (sky, far, middle, near, foreground details) and the palette.
|
|
40
|
+
2. **Block in.** Big shapes with `vector.addShape` and `vector.addPath`, flat colours, one
|
|
41
|
+
`run_commands` per group with `atomic: true`; `vector.group` each object with a `name`.
|
|
42
|
+
**Check:** `vector.list` order is back to front; names say what each group is.
|
|
43
|
+
3. **Form.** Shade with gradients rather than extra shapes: `vector.setStyle` `fill` with a
|
|
44
|
+
`linear` or `radial` gradient (`interpolation: "oklab"` for clean blends between saturated
|
|
45
|
+
colours), `vector.setGradient` to place it, `vector.setMeshGradient` or
|
|
46
|
+
`vector.setFreeformGradient` for organic shading. For a highlight or a texture over a fill,
|
|
47
|
+
`vector.addFill` on the same path with its own `opacity` and `blendMode` (screen to lighten,
|
|
48
|
+
multiply to darken).
|
|
49
|
+
**Check:** `vector.getAppearance` shows the stack; the shape count did not grow.
|
|
50
|
+
4. **Masks for art inside shapes.** Put the shape that frames the art on top and
|
|
51
|
+
`vector.makeClippingMask` with the art below it (a pattern in a window, a scene in a circle);
|
|
52
|
+
`vector.makeOpacityMask` with a white-to-black gradient for fades and reflections.
|
|
53
|
+
**Check:** `vector.list` shows the mask group's `mask` with the right `contents`.
|
|
54
|
+
5. **Transitions and repeats.** `vector.blend` between two shapes for smooth steps (a sunset of
|
|
55
|
+
bands, a row of fading circles); symbols for repeated objects (trees, stars).
|
|
56
|
+
6. **Shadows and glows.** `vector.addFilter` `dropShadow` (`offsetX` 0 and `offsetY` positive for
|
|
57
|
+
light from above), `outerGlow` for light sources, `layerBlur` for depth of field.
|
|
58
|
+
**Check:** `vector.list` `filters` show one light direction across the design.
|
|
59
|
+
7. **Finish and export.** Review the stack, then `vector.exportSvg` (or `vector.exportFrameSvg`
|
|
60
|
+
per artboard). Expand live effects (`vector.expandAppearance`, `vector.expandBlend`) only for
|
|
61
|
+
targets that need plain SVG.
|
|
62
|
+
|
|
63
|
+
## Decision table
|
|
64
|
+
|
|
65
|
+
| Want | Approach |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| Roundness on a ball, sun or face | Radial gradient, light stop toward the light |
|
|
68
|
+
| Sky or ground fading | Linear gradient, two or three stops |
|
|
69
|
+
| Organic shading (fabric, a face) | Mesh or freeform gradient |
|
|
70
|
+
| A highlight or texture over a colour | `vector.addFill` stacked on the same path, screen or overlay |
|
|
71
|
+
| An outline around an outline | `vector.addStroke` stacked, each with its own `strokeAlign` |
|
|
72
|
+
| Art visible only inside a shape | `vector.makeClippingMask` (the frame on top) |
|
|
73
|
+
| A fade-out or reflection | `vector.makeOpacityMask` with a white-to-black gradient |
|
|
74
|
+
| Even steps between two shapes | `vector.blend` |
|
|
75
|
+
| Card or object lifted off the page | `vector.addFilter` `dropShadow`, subtle |
|
|
76
|
+
|
|
77
|
+
## Recipes
|
|
78
|
+
|
|
79
|
+
**Radial shading.** `vector.setStyle` with `fill: { type: "radial", interpolation: "oklab",
|
|
80
|
+
stops: [{ offset: 0, color: light }, { offset: 1, color: dark }] }`; move the centre toward the
|
|
81
|
+
light with `vector.setGradient`.
|
|
82
|
+
|
|
83
|
+
**Pattern in a window.** Draw the pattern, draw the window shape on top, then
|
|
84
|
+
`vector.makeClippingMask` with all their ids: the window clips, its own fill is not drawn, and
|
|
85
|
+
the pattern stays movable inside.
|
|
86
|
+
|
|
87
|
+
**Subtle shadow.** `vector.addFilter` `{ type: "dropShadow", params: { color: "#000000",
|
|
88
|
+
opacity: 0.2, offsetX: 0, offsetY: 4, blur: 8 } }` on the object.
|
|
89
|
+
|
|
90
|
+
## Pitfalls
|
|
91
|
+
|
|
92
|
+
- **Duplicates for shading.** A copied shape for a highlight drifts when the original is edited;
|
|
93
|
+
stack a fill instead.
|
|
94
|
+
- **Muddy gradients.** sRGB interpolation between saturated hues passes through grey; use
|
|
95
|
+
`oklab`, or add a midpoint stop.
|
|
96
|
+
- **Mixed light directions** make a scene look collaged; keep every shadow offset the same way.
|
|
97
|
+
- **Heavy shadows.** Dark, hard, far shadows look pasted on.
|
|
98
|
+
- **The mask on the wrong layer.** `vector.makeClippingMask` uses the topmost object as the mask
|
|
99
|
+
unless `maskId` says otherwise; put the frame on top first.
|
|
100
|
+
- **Filters in exports.** SVG filters render differently in some tools; expand or rasterize where
|
|
101
|
+
the result must look identical everywhere.
|
|
102
|
+
|
|
103
|
+
## Tools used
|
|
104
|
+
|
|
105
|
+
- `vector.list`, `vector.getAppearance`: structure, order, fills, masks and filters
|
|
106
|
+
- `vector.addShape`, `vector.addPath`, `vector.group`, `vector.moveLayer`: block in, group and order
|
|
107
|
+
- `vector.setStyle`, `vector.setGradient`, `vector.setMeshGradient`, `vector.setFreeformGradient`: colour and gradients
|
|
108
|
+
- `vector.addFill`, `vector.addStroke`, `vector.setBlendMode`: appearance stacks
|
|
109
|
+
- `vector.makeClippingMask`, `vector.makeOpacityMask`, `vector.setMaskOptions`: masks
|
|
110
|
+
- `vector.blend`, `vector.makeSymbol`, `vector.placeSymbol`: transitions and repeats
|
|
111
|
+
- `vector.addFilter`, `vector.updateFilter`: shadows, glows and blur
|
|
112
|
+
- `vector.expandAppearance`, `vector.expandBlend`, `vector.exportSvg`, `vector.exportFrameSvg`: finish and export
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Show the stripes only inside the round window, and keep the stripes movable inside it.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 100,
|
|
6
|
+
"height": 100,
|
|
7
|
+
"setup": [
|
|
8
|
+
{ "id": "vector.addShape", "params": { "kind": "rect", "x": 10, "y": 10, "width": 16, "height": 80, "name": "Stripe", "fill": "#457b9d", "stroke": null } },
|
|
9
|
+
{ "id": "vector.addShape", "params": { "kind": "rect", "x": 42, "y": 10, "width": 16, "height": 80, "name": "Stripe", "fill": "#457b9d", "stroke": null } },
|
|
10
|
+
{ "id": "vector.addShape", "params": { "kind": "rect", "x": 74, "y": 10, "width": 16, "height": 80, "name": "Stripe", "fill": "#457b9d", "stroke": null } },
|
|
11
|
+
{ "id": "vector.addShape", "params": { "kind": "ellipse", "x": 20, "y": 20, "width": 60, "height": 60, "name": "Window", "fill": "#ffffff", "stroke": null } }
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"checks": [
|
|
15
|
+
{ "why": "a clipping mask using the window, with all three stripes inside", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$some": { "mask": { "type": "clip", "enabled": true, "contents": { "$length": 3 } } } } } } },
|
|
16
|
+
{ "why": "the stripes are still separate, editable paths", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$expr": "v.filter((n) => n.name === 'Stripe' && n.type === 'path').length === 3" } } } }
|
|
17
|
+
],
|
|
18
|
+
"solution": [
|
|
19
|
+
{ "tool": "run_command", "args": { "id": "vector.makeClippingMask", "params": { "ids": { "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.*.id" } } } }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Give the sun a soft radial gradient: yellow in the middle fading to orange at the edge.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 100,
|
|
6
|
+
"height": 100,
|
|
7
|
+
"setup": [{ "id": "vector.addShape", "params": { "kind": "ellipse", "x": 30, "y": 30, "width": 40, "height": 40, "name": "Sun", "fill": "#ffcc00", "stroke": null } }]
|
|
8
|
+
},
|
|
9
|
+
"checks": [
|
|
10
|
+
{ "why": "a radial gradient from yellow at the centre to orange at the edge", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$some": { "name": "Sun", "fill": { "$expr": "v && v.type === 'radial' && v.stops.length >= 2 && v.stops[0].offset === 0 && v.stops[v.stops.length - 1].offset === 1" } } } } } },
|
|
11
|
+
{ "why": "the sun is still the same shape and place", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$some": { "name": "Sun", "bounds": { "x": 30, "y": 30, "width": 40, "height": 40 } } } } } }
|
|
12
|
+
],
|
|
13
|
+
"solution": [
|
|
14
|
+
{ "tool": "run_command", "args": { "id": "vector.setStyle", "params": { "ids": { "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.*.id" }, "fill": { "type": "radial", "interpolation": "oklab", "stops": [{ "offset": 0, "color": "#ffe14d" }, { "offset": 1, "color": "#ff8c1a" }] } } } }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Add a subtle drop shadow under the card, with the light coming from above.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 200,
|
|
6
|
+
"height": 140,
|
|
7
|
+
"setup": [{ "id": "vector.addShape", "params": { "kind": "rect", "x": 40, "y": 30, "width": 120, "height": 80, "cornerRadius": 8, "name": "Card", "fill": "#ffffff", "stroke": null } }]
|
|
8
|
+
},
|
|
9
|
+
"checks": [
|
|
10
|
+
{ "why": "a soft, faint shadow falling straight down", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$some": { "name": "Card", "filters": { "$some": { "type": "dropShadow", "params": { "opacity": { "$min": 0.08, "$max": 0.35 }, "offsetX": { "$min": -1, "$max": 1 }, "offsetY": { "$min": 2, "$max": 10 }, "blur": { "$min": 3 } } } } } } } } },
|
|
11
|
+
{ "why": "drawn as an effect: still one card, no extra shadow shape", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$length": 1 } } } }
|
|
12
|
+
],
|
|
13
|
+
"solution": [
|
|
14
|
+
{ "tool": "run_command", "args": { "id": "vector.addFilter", "params": { "ids": { "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.*.id" }, "type": "dropShadow", "params": { "color": "#000000", "opacity": 0.2, "offsetX": 0, "offsetY": 4, "blur": 8 } } } }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-vector-logo
|
|
3
|
+
description: The logo workflow in the Pixerate vector editor - building a mark from primitives with live booleans, Pathfinder and Shape Builder, optical corrections, a lockup of mark and wordmark aligned and spaced on the artboard, text converted to outlines for the final file, colour variants (full colour, one colour, reversed) through symbols, and clean single-path SVG export. Use when asked to design, draw, redraw, vectorise, clean up or export a logo, logomark, wordmark, monogram, emblem or brand mark, make a one-colour or reversed version, or prepare a logo file for print or the web, or when vector.boolean, vector.pathfinder, vector.createOutlines or vector.exportSvg are available. Load with pixerate-vector, which lists every command.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-vector
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Making a logo
|
|
10
|
+
|
|
11
|
+
A logo has to survive every size and every background, so it is built from few, clean shapes,
|
|
12
|
+
kept editable while it is being designed and flattened only for delivery. Design with live
|
|
13
|
+
booleans and real text; deliver plain paths with text outlined, one file per variant.
|
|
14
|
+
|
|
15
|
+
This skill is the method. `pixerate-vector` has the full command reference; run the commands
|
|
16
|
+
with `run_command`, or several as one undo step with `run_commands` and `atomic: true`.
|
|
17
|
+
|
|
18
|
+
## Goal and quality bar
|
|
19
|
+
|
|
20
|
+
- The mark is built from simple geometry: primitives combined with booleans, with as few anchors
|
|
21
|
+
as the shape needs.
|
|
22
|
+
- It reads at 16 px and at poster size: no hairlines thinner than about 1/30 of the mark's
|
|
23
|
+
height, no details that close up when small.
|
|
24
|
+
- The lockup is aligned on the artboard: mark and wordmark centred (or aligned to a shared edge),
|
|
25
|
+
with a deliberate gap kept in every variant.
|
|
26
|
+
- Delivery files are self-contained: text outlined (`vector.createOutlines`), live booleans and
|
|
27
|
+
effects expanded, one path per colour where possible, no hidden or stray nodes.
|
|
28
|
+
- Variants match: full colour, one colour (black) and reversed (white on dark) are the same
|
|
29
|
+
geometry.
|
|
30
|
+
|
|
31
|
+
## Inputs to confirm
|
|
32
|
+
|
|
33
|
+
- **Name and wording** for the wordmark, and the typeface (`vector.listFonts`).
|
|
34
|
+
- **Colours:** the brand palette as hex, and which variants are needed.
|
|
35
|
+
- **Stage:** still designing (keep live) or delivering (flatten and outline).
|
|
36
|
+
|
|
37
|
+
## Working method
|
|
38
|
+
|
|
39
|
+
1. **Read.** `vector.list`: the artboard, existing shapes and text, their fills and bounds.
|
|
40
|
+
**Check:** you know which nodes are the mark and which the wordmark.
|
|
41
|
+
2. **Build the mark from primitives.** `vector.addShape` (rect, ellipse, polygon, star) on round
|
|
42
|
+
numbers; combine with `vector.boolean` and `live: true` while designing (`union` for one
|
|
43
|
+
silhouette, `subtract` for cut-outs, `intersect`); `vector.pathfinder` when you need every
|
|
44
|
+
region (`divide`) or `vector.shapeBuilder` to merge chosen regions.
|
|
45
|
+
**Check:** `vector.list` shows the live group with its `liveBoolean` and the expected bounds.
|
|
46
|
+
3. **Correct optically.** Round shapes overshoot flat ones by 2-3 % so they look the same height;
|
|
47
|
+
a horizontal bar looks thicker than a vertical one of equal width, so thin it slightly;
|
|
48
|
+
centre by eye mass (a triangle's centroid, not its box).
|
|
49
|
+
4. **Set the wordmark** with `vector.addText` (point text) and `vector.setTextStyle` for tracking;
|
|
50
|
+
keep it as text while designing.
|
|
51
|
+
**Check:** the text result's `overflow` is empty and `missingFonts` lists nothing.
|
|
52
|
+
5. **Lock it up.** Position mark and wordmark with `vector.transform` (`dx`, `dy`) or
|
|
53
|
+
`vector.align`, then group them (`vector.group` with a `name`).
|
|
54
|
+
**Check:** `vector.list` bounds: centres equal (or edges aligned), the gap what you intended.
|
|
55
|
+
6. **Variants.** Make the lockup a symbol (`vector.makeSymbol`) and place it once per variant
|
|
56
|
+
(`vector.placeSymbol`), recolouring with `vector.setInstanceOptions` `fill`, so a geometry
|
|
57
|
+
change updates every variant. Check each against its background colour (contrast).
|
|
58
|
+
7. **Deliver.** For each variant: `vector.createOutlines` on the text, `vector.expandAppearance`
|
|
59
|
+
on live booleans and effects (or `vector.boolean` without `live` for one path),
|
|
60
|
+
`vector.removeRedundantPoints`, then `vector.exportSvg` with `precision` 2.
|
|
61
|
+
**Check:** `vector.list` has only plain paths; the SVG has no `text` and no filters.
|
|
62
|
+
|
|
63
|
+
## Decision table
|
|
64
|
+
|
|
65
|
+
| Want | Command |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| One silhouette from overlapping shapes, still editable | `vector.boolean` `union`, `live: true` |
|
|
68
|
+
| A hole or notch | `vector.boolean` `subtract` (top shape cuts the one below) |
|
|
69
|
+
| Every overlap as its own piece | `vector.pathfinder` `divide` |
|
|
70
|
+
| Merge or remove particular regions | `vector.shapeBuilder` |
|
|
71
|
+
| A ring or donut for export | `vector.boolean` `subtract` without `live`: one path with two subpaths |
|
|
72
|
+
| Same logo in several colours | A symbol, with `vector.setInstanceOptions` per variant |
|
|
73
|
+
| Text that must not depend on fonts | `vector.createOutlines` (keep a text copy while designing) |
|
|
74
|
+
|
|
75
|
+
## Recipes
|
|
76
|
+
|
|
77
|
+
**Live union.** `vector.boolean` `{ operation: "union", live: true, ids }` with the parts'
|
|
78
|
+
ids from `vector.list`; adjust a part later by its own id inside the group.
|
|
79
|
+
|
|
80
|
+
**Knockout for export.** `vector.boolean` `{ operation: "subtract", ids: [outer, inner] }`: one
|
|
81
|
+
path, the hole see-through (not painted white).
|
|
82
|
+
|
|
83
|
+
**Centre a lockup.** For each part, `dx = artboard.width / 2 - (bounds.x + bounds.width / 2)`
|
|
84
|
+
with `vector.transform`; leave `dy` at 0 to keep the vertical gap.
|
|
85
|
+
|
|
86
|
+
## Pitfalls
|
|
87
|
+
|
|
88
|
+
- **White shapes as holes.** A white circle over a red one is not a hole; it shows on a coloured
|
|
89
|
+
background. Subtract it.
|
|
90
|
+
- **Flattening too early.** A destructive boolean or outlined text cannot be re-edited; keep live
|
|
91
|
+
versions until delivery.
|
|
92
|
+
- **Live effects in delivered files.** Filters and live booleans export as SVG filters or
|
|
93
|
+
Pixerate attributes that other tools may not read; expand first.
|
|
94
|
+
- **Missing fonts.** A family the shaper lacks draws in a fallback; check `missingFonts` before
|
|
95
|
+
outlining, since outlines bake whatever was drawn.
|
|
96
|
+
- **Hairlines and tiny counters** disappear at favicon size; test the mark small.
|
|
97
|
+
|
|
98
|
+
## Tools used
|
|
99
|
+
|
|
100
|
+
- `vector.list`, `vector.getText`, `vector.listFonts`: read shapes, text and fonts
|
|
101
|
+
- `vector.addShape`, `vector.addPath`, `vector.addText`, `vector.setTextStyle`: build the mark and wordmark
|
|
102
|
+
- `vector.boolean`, `vector.pathfinder`, `vector.shapeBuilder`: combine shapes
|
|
103
|
+
- `vector.transform`, `vector.align`, `vector.group`: lock up and space
|
|
104
|
+
- `vector.makeSymbol`, `vector.placeSymbol`, `vector.setInstanceOptions`: colour variants
|
|
105
|
+
- `vector.setStyle`: brand colours
|
|
106
|
+
- `vector.createOutlines`, `vector.expandAppearance`, `vector.removeRedundantPoints`: prepare for delivery
|
|
107
|
+
- `vector.exportSvg`: the delivered file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Centre the logo mark and the wordmark bar under it horizontally on the artboard, keeping the gap between them.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 120,
|
|
6
|
+
"height": 80,
|
|
7
|
+
"setup": [
|
|
8
|
+
{ "id": "vector.addShape", "params": { "kind": "ellipse", "x": 14, "y": 8, "width": 32, "height": 32, "name": "Mark", "fill": "#1d3557", "stroke": null } },
|
|
9
|
+
{ "id": "vector.addShape", "params": { "kind": "rect", "x": 30, "y": 52, "width": 64, "height": 12, "name": "Wordmark", "fill": "#1d3557", "stroke": null } }
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"checks": [
|
|
13
|
+
{ "why": "both centred on the artboard", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": { "$length": 2, "$every": { "bounds": { "$expr": "Math.abs(v.x + v.width / 2 - 60) <= 0.5" } } } } } },
|
|
14
|
+
{ "why": "the vertical layout and gap are unchanged", "tool": "run_command", "args": { "id": "vector.list" }, "expect": { "result": { "nodes": [{ "name": "Mark", "bounds": { "y": 8, "height": 32 } }, { "name": "Wordmark", "bounds": { "y": 52, "height": 12 } }] } } }
|
|
15
|
+
],
|
|
16
|
+
"solution": [
|
|
17
|
+
{ "tool": "run_command", "args": { "id": "vector.transform", "params": { "ids": [{ "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.0.id" }], "dx": 30 } } },
|
|
18
|
+
{ "tool": "run_command", "args": { "id": "vector.transform", "params": { "ids": [{ "$read": "run_command", "args": { "id": "vector.list" }, "path": "result.nodes.1.id" }], "dx": -2 } } }
|
|
19
|
+
]
|
|
20
|
+
}
|