mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 17:32:42 -06:00
Merge main into feat/script-console
This commit is contained in:
@@ -102,7 +102,7 @@ the two can still win that race.
|
||||
| `ui.click` / `ui.drag` | `{x, y, button?, count?, shift?, alt?, ctrl?, cmd?}` / `{x, y, toX, toY, steps?, count?, button?, shift?, alt?, ctrl?, cmd?}` | egui pointer input; see synthetic input budgets above |
|
||||
| `ui.wheel` | `{x, y, dy?, dx?, unit?: "line"\|"point", shift?, alt?, cmd?}` | a mouse wheel turn over screen point (x, y): `dy` notches up (+) or down, `dx` sideways. Over the canvas the wheel scrolls, Cmd-wheel (Ctrl on Windows and Linux) scrolls sideways and Alt-wheel (Option on the Mac) zooms about the pointer (with the `ctrlWheelScrollsSideways` preference off, Cmd-wheel zooms too); with the `zoomWithMouseWheel` preference the wheel and Alt-wheel zoom about the pointer, Shift-wheel scrolls up and down and Cmd-wheel (Ctrl on Windows and Linux) sideways. Over a focused numeric field (click it first) each notch steps its value as Up/Down do (Shift: ten, Cmd/Ctrl: a tenth) and the panel stays put; over anything else in a panel the wheel scrolls it. Over the canvas, the wheel preferences in `docs/mcp.md` change what these keys do |
|
||||
| `ui.set` | `{brightness?, panel?, rulers?, outline?, grid?, smartGuides?, boundingBox?, controlBar?, imageTracePreview?}` | `outline`: View › Outline for the active document only (each document has its own, a document opens in Preview, and `ui.inspect` shows it as `view.outline`; an error without a document). `imageTracePreview`: the Image Trace panel's Preview (`ui.inspect` shows it as `ui.image_trace_preview`) |
|
||||
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value}` or `{fields: [{field, value}…]}` | fill and submit the open dialog. `ui.dialog.set` takes the fields the dialog has (`ui.inspect`), `__` fields, `discard` on a dialog with a discard button, `preview` on one with a live preview, the fields that the dialog reads but adds only when a control changes (such as `searchFolder`, or `copy` in the Transform dialogs), and the fields it passes to the command it runs: every parameter of the `command` dialog's command (`ui.paramDialog`), and the `textureImage` that Load Texture… sets in the Glass and Texturizer effects; any other field is an error that lists the dialog's fields, and the dialog stays as it was `fields` sets the list (at most 256 items) in order (a missing `value` is `null`) and checks every item first: when one is refused, the call is an error and no field changes |
|
||||
| `ui.dialog.set` / `.confirm` / `.cancel` | `{field, value, action?}` or `{fields: [{field, value}…], action?}` | fill and submit the open dialog. `ui.dialog.set` takes the fields the dialog has (`ui.inspect`), `__` fields, `discard` on a dialog with a discard button, `preview` on one with a live preview, the fields that the dialog reads but adds only when a control changes (such as `searchFolder`, or `copy` in the Transform dialogs), and the fields it passes to the command it runs: every parameter of the `command` dialog's command (`ui.paramDialog`), and the `textureImage` that Load Texture… sets in the Glass and Texturizer effects; any other field is an error that lists the dialog's fields, and the dialog stays as it was `fields` sets the list (at most 256 items) in order (a missing `value` is `null`) and checks every item first: when one is refused, the call is an error and no field changes. `action: "confirm"` or `"cancel"` on `ui.dialog.set` then confirms or cancels the same dialog in the same request, as `ui.dialog.confirm` and `ui.dialog.cancel` do, and the reply is `{dialog, result}`: the dialog after the fields were set and the reply to confirm (`null` for cancel); any other `action` is an error and no field changes |
|
||||
| `ui.screenshot` | `{path?, data?}` | capture the window (PNG), written to `path`; `data: true` sends it back as `pngBase64` too. The inline PNG is capped at 64 MiB: with a `path` the file is still written and `pngBase64` is omitted when the image is larger, while a path-less `data: true` request fails if either the raw RGBA image or the encoded PNG exceeds the cap. Needs a presented frame: with the screen locked or the window minimized/covered it fails after ~8 s with an explanatory error |
|
||||
| `ui.render` | `{path?, scale?, artboard?, data?, traceViews?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first; `traceViews: true`: Image Trace objects draw in their View, as on the canvas, instead of their tracing result): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
|
||||
| `ui.render` | `{path?, scale?, artboard?, data?}` | render an artboard headlessly (PNG with the pixels of `app.export {format: "png", background: "white"}` at the same scale; `artboard`: its 0-based index, default the first): written to `path`, else (or with `data: true` as well) sent back as `pngBase64` |
|
||||
|
||||
+36
-8
@@ -460,7 +460,7 @@ opens the picker through `run_command` with the remote backend.
|
||||
| `set_paint` | `{fill?, stroke?, strokeWidth?, ids?}` | Applies to the selection (or `ids`) and becomes the default for new art. Returns `{applied, objects}`: `objects` is the number of objects painted, as the `objects` of the `paint.setFill`, `paint.setStroke` and `stroke.set` replies (the largest of them): a selected group counts the objects inside it. When it is 0, a `note` says that only the paint for new art changed. |
|
||||
| `press_key` | `{key, mods?}` | Remote: a real key event. Headless: runs the command or tool bound to that shortcut, or sends the key to the busy tool (digits too: `5` while dragging with the Perspective Selection tool; `e` while dragging a Selection or Group Selection marquee; `c` and `f` while dragging out an arc; `f`, `v`, `x` and `c` while dragging out a rectangular or polar grid). |
|
||||
| `type_text` | `{text}` | Remote only. |
|
||||
| `answer_dialog` | `{set?: [{field, value?}], action?}` | Remote only. Sets fields of the app's open dialog in the order given with one `ui.dialog.set {fields}` (a missing `value` is `null`). The app stores all of them or none. When the app refuses the `ui.dialog.set`, for example because no dialog is open, the call is an error and `action` is not sent. Then `action` `confirm` (OK, `ui.dialog.confirm`) or `cancel` (`ui.dialog.cancel`). Give `set`, `action` or both; `set` holds at most 256 fields. `inspect_ui` shows the open dialog and its fields as `ui.dialog`. Returns `{dialog, result}`: the dialog after the fields were set (or `null`) and the reply to `confirm` or `cancel` (or `null`). After `invoke_menu` `app.quit`, `file.close` or `file.closeAll` replies `{"pending": "saveChanges"}`, `{action: "confirm"}` saves, `{set: [{field: "discard", value: true}], action: "confirm"}` closes without saving and `{action: "cancel"}` cancels the close or quit. |
|
||||
| `answer_dialog` | `{set?: [{field, value?}], action?}` | Remote only. Sets fields of the app's open dialog in the order given with one `ui.dialog.set {fields, action?}` (a missing `value` is `null`). The app stores all of them or none, then answers that same dialog with `action` in the same request: `confirm` (OK) or `cancel`. Another connection cannot open a dialog between the two steps. When the app refuses the `ui.dialog.set`, for example because no dialog is open, the call is an error and the dialog is not confirmed or canceled. `action` alone is sent as `ui.dialog.confirm` or `ui.dialog.cancel`. Give `set`, `action` or both; `set` holds at most 256 fields. `inspect_ui` shows the open dialog and its fields as `ui.dialog`. Returns `{dialog, result}`: the dialog after the fields were set (or `null`) and the reply to `confirm` or `cancel` (or `null`). After `invoke_menu` `app.quit`, `file.close` or `file.closeAll` replies `{"pending": "saveChanges"}`, `{action: "confirm"}` saves, `{set: [{field: "discard", value: true}], action: "confirm"}` closes without saving and `{action: "cancel"}` cancels the close or quit. |
|
||||
| `invoke_menu` | `{command, params?}` | Invokes a menu item by command id. Includes UI commands such as `view.*` and `window.*` in remote mode. |
|
||||
| `open_panel` | `{panel}` | Remote only. `panel` is a panel id (`layers`, `swatches`, `colorGuide`, …, as `window.panel` takes) or its display label (`"Color Guide"`), in any case. |
|
||||
| `screenshot` | `{path?, scale?, artboard?, window?, max_side?, traceViews?}` | Returns MCP image content (`image/png`, base64) plus a text block. Renders the artboard with the pixels of a PNG `export` with `background: "white"` at the same scale; `traceViews:true` draws Image Trace objects in the view set with `imageTrace.setView`, as in the app's window; `window:true` captures the app window (remote only), as `ui_screenshot` does, and `max_side` (1–4096, window captures only) scales the returned image down as described for `ui_screenshot`. With `path` the backend (the headless session or the app) writes the PNG there too; a window capture written there keeps its full size with `max_side`. |
|
||||
@@ -1371,7 +1371,7 @@ naming the key, and change nothing: `radius` in `shape.rectangle`; `sides` and `
|
||||
in `shape.flare`; `decay` and `segments` in `shape.spiral`; `rows` and `columns` in `shape.rectangularGrid`; `concentric`
|
||||
and `radial` in `shape.polarGrid`; `size` and the `width`, `height` and `fitMinPercent` of `area` in `text.create`;
|
||||
`size` and `fitMinPercent` in `text.createInPath`; `start`, `end`, `size`, `tracking`,
|
||||
`baselineShift`, `hScale`, `vScale`, `rotation` and `strokeWidth` in `text.setRangeStyle`; `start` and `end` in
|
||||
`baselineShift`, `hScale`, `vScale`, `rotation` and `strokeWidth` in `text.setRangeStyle`; `strokeWidth` in `text.setStyle`; `start` and `end` in
|
||||
`text.editRange` and `text.getRange`; `at` in `text.startList`; `start`, `hScale`, `vScale` and `rotation` in `text.touchType`; `start` in
|
||||
`text.glyphAlternates` and `text.setGlyphAlternate`; `by`, `start` and
|
||||
`end` in `type.step`; `by` in `type.size.increase` and `type.size.decrease`; `size` in `type.ruby`; `width`, `height`
|
||||
@@ -1383,6 +1383,15 @@ and `amount` in `color.harmony`; `angle` in `paint.editGradient` and in the `gra
|
||||
`width` and `tolerance` in `livePaint.strokeEdge`. A null is read as a key left out. A gradient stop that names a
|
||||
swatch refuses a `tint` that is not a number with `` stop 0: `tint` must be a number ``.
|
||||
|
||||
`text.setStyle` refuses a `size` that is neither a number nor a length with a unit (`"12 pt"`, `"5 mm"`) with
|
||||
`` `size` must be a number, not true ``, and a `tracking` that is neither a number nor a number written as a string
|
||||
(`"20"`) with `` `tracking` must be a number, not false ``. It and `text.setRangeStyle` refuse a `font` or `style` that is not a string
|
||||
with `` `font` must be a string ``, and a `features` list with an entry that is not a string with
|
||||
`features must be a list of tags`; `text.setStyle` refuses such a `justify` too. `type.ruby` refuses a `text`,
|
||||
`position` or `align` that is not a string, and `text.setPathHandle` refuses a `which` other than `in` or `out` with
|
||||
`` `which` must be one of in, out ``. Each leaves the document, the selection and the undo history as they were, also
|
||||
when the call gives another param it could apply. A null is read as a key left out.
|
||||
|
||||
A byte offset must be a whole number of at least 0. `at` in `text.startList`, and `start` and `end` in
|
||||
`text.editRange`, `text.getRange` and `text.setRangeStyle`, and in `type.step` and `addon.type.textTweaker` given an `id`,
|
||||
refuse a negative or fractional number with
|
||||
@@ -2635,7 +2644,8 @@ Every 3D type, `threeD.classicExtrude` and `threeD.revolve` included, also appli
|
||||
symbol instance, alone or in a group, and builds from the art it shows, the same way for the canvas, exports and
|
||||
`assets.add3D` ([three-d.md](three-d.md#clipping-groups-blends-and-symbol-instances)); like a group, it takes the
|
||||
effect on the object itself, not on a fill or stroke. An image, alone or in a group, is refused with "trace it first
|
||||
with Object › Image Trace › Make", and nothing changes.
|
||||
with Object › Image Trace › Make", and nothing changes. On a group, an opacity mask on a member, or on a group inside
|
||||
it, masks that member's solids ([three-d.md](three-d.md#groups)).
|
||||
|
||||
`threeD.extrude`: `depth` (points, 0..2000, default 50), `twist` (degrees, 0..360, 0), `taper` (1..100%, 100),
|
||||
`cap` (true solid, false hollow), `bevel` (`none`, `classic`, `rounded`, `cove`, `step`, `ogee`), `bevelWidth`
|
||||
@@ -3121,6 +3131,14 @@ the old name. A rename gives the new name to the pattern's swatches that had the
|
||||
old name keeps it. A blank name gives "New Pattern N" in `object.pattern.make` and "`<pattern>` copy" in
|
||||
`object.pattern.saveCopy`, and `pattern.options` keeps the name.
|
||||
|
||||
The pattern commands (`object.pattern.make`, `object.pattern.edit`, `object.pattern.saveCopy`, `pattern.options`,
|
||||
`pattern.delete`) and the Repeat commands (`object.repeat.radial`, `object.repeat.grid`, `object.repeat.mirror`,
|
||||
`object.repeat.options`) reject a param of the wrong type, such as `"instances": "8"`, `"edit": "false"` or
|
||||
`"tileType": 2`, and change nothing. `pattern.options` also rejects an `overlap` that is not an object, and an
|
||||
`overlap.h` other than `left` or `right` or an `overlap.v` other than `top` or `bottom`. `object.repeat.options` rejects
|
||||
a `center` that is not `[x, y]`, and `object.repeat.radial` rejects a negative `radius`. Omitting a param or giving null
|
||||
keeps its default.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"pattern.options","params":{"showSwatchBounds":true}}}
|
||||
{"name":"run_command","arguments":{"command":"prefs.set","params":{"key":"patternTileEdgeColor","value":"#ff4f4f"}}}
|
||||
@@ -3621,7 +3639,9 @@ Selected End Points) joins the ends again; `path.join {ids: [a, b], ends: ["last
|
||||
(Average & Join, Cmd+Alt+Shift+J) moves two ends to their midpoint and joins them there, the joined anchor a corner or a smooth point, in one
|
||||
undo step: the two direct-selected anchors when they are open path ends, else the nearest ends of the two open paths in `ids` or the
|
||||
selection (both ends of one, closing it); anything else is refused. `path.convertAnchor {id, subpath?, anchor, to, x?, y?}` and
|
||||
`path.split {id, subpath?, anchor}` do the same to one anchor (the Anchor Point and Scissors tools); the Pen with Alt
|
||||
`path.split {id, subpath?, anchor}` do the same to one anchor (the Anchor Point and Scissors tools); `path.split`,
|
||||
at an anchor or at `segment` and `t`, leaves the first anchor of the piece after the cut selected alone, and a Direct
|
||||
Selection press on two anchors on top of each other takes the selected one; the Pen with Alt
|
||||
held over a selected path's handle, anchor or segment works as the Anchor Point tool. `press_key` Escape between two
|
||||
`pointer_gesture` calls of an Anchor Point drag cancels it: the path goes back and no undo step is added. `path.reshapeSegment {id,
|
||||
subpath?, segment, t, dx, dy, perpendicular?}` (a segment dragged with Direct Selection or the Anchor Point tool) moves the segment's
|
||||
@@ -4523,7 +4543,8 @@ four: General (rulers, positions and sizes, the Info panel, dialog distances, ca
|
||||
(weights, dashes), Type (font size, leading, baseline shift) and East Asian Type (no field uses it yet). General is the active document's units
|
||||
(`document.inspect` → `units`): `document.setUnits {units}` (Document Setup) sets it for that document, and
|
||||
`prefs.set {key: "unitsGeneral", value}` sets it for the active document too (one undo step) and is the units
|
||||
`file.new` starts in when it gets no `units`. Stroke, Type and East Asian Type are the preferences `unitsStroke`,
|
||||
`file.new` starts in when it gets no `units`. With `documentUnits: false`, `prefs.set` sets only the preference and
|
||||
leaves the document's units as they are; Reset Preferences followed by OK in the Preferences dialog does this. Stroke, Type and East Asian Type are the preferences `unitsStroke`,
|
||||
`unitsType` and `unitsAsianType`. Only `unitsAsianType` also takes `q` (Q, `Q`) and `ha` (Ha, `Ha`), each a quarter of
|
||||
a millimetre (0.709 pt); the other preferences, `document.setUnits` and the rulers do not list them, and it is Points
|
||||
by default. A length typed with a unit
|
||||
@@ -4548,7 +4569,10 @@ a string with a unit. Unit names: `Points`, `Picas`, `Inches`, `Millimeters`, `C
|
||||
settings whose label, section or category contains it, ignoring case, in the interface language or in English, or by
|
||||
"No settings found". Choosing a setting sets `__category` to its category, sets `__found` to its key (the row is
|
||||
highlighted until another category is shown) and clears `__search`. Cmd/Ctrl+F focuses the field again; Escape in the
|
||||
field with text clears it, and Escape in the empty field closes the dialog.
|
||||
field with text clears it, and Escape in the empty field closes the dialog. Its Reset Preferences button opens a
|
||||
`resetPreferences` question (fields `message`, `detail`) over Preferences: `ui.dialog.cancel` shows Preferences again
|
||||
as it was, and `ui.dialog.confirm` shows it again with every preference at its default value, applied by OK in
|
||||
Preferences. `prefs.reset` resets the preferences at once, without the question.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"prefs.set","params":{"key":"unitsGeneral","value":"millimeters"}}}
|
||||
@@ -7510,8 +7534,12 @@ bounding-box handles scale it in plane space (`perspective.transform`; Shift kee
|
||||
centre), and `press_key` `5` during a drag switches the move to perpendicular to the plane (press again to switch
|
||||
back). Holding Shift during an in-plane move constrains each object's displacement to an axis or 45° diagonal of its
|
||||
own plane. `perspective.move` accepts `constrain: true` (default false) and adjusts Snap to Grid along the constrained
|
||||
direction on the first object's plane and depth. With Snap to Grid, every object on the first object's plane moves
|
||||
by the same plane offset, whatever its depth, and the edge of their joint bounds nearer a gridline lands on it.
|
||||
direction on the first object's plane and depth. With Snap to Grid, an in-plane move moves every object on the first
|
||||
object's plane by the same plane offset, whatever its depth. Without `constrain`, on each axis, the edge of their joint
|
||||
bounds nearer a gridline lands on it when that edge ends within a quarter cell of the gridline. With `constrain`, an
|
||||
edge also lands only when it ends within a quarter cell of a gridline: a horizontal or vertical move can land only an
|
||||
edge on its own axis, and a 45° move lands the edge on the axis whose nonzero correction is smaller. When no edge on
|
||||
those axes is that close, the move does not snap. A perpendicular move does not snap.
|
||||
`press_key` Escape during a move, a resize or a plane widget drag cancels it and puts the objects and the
|
||||
grid back. The arrow keys nudge a selection in perspective by the keyboard increment (`perspective.nudge`; Shift ×10, Alt
|
||||
copies). Object › Transform › Transform Again repeats the last perspective move or scale in plane space.
|
||||
|
||||
+4
-3
@@ -6,9 +6,10 @@ Select a path, compound path, type, clipping group, blend, symbol instance, or a
|
||||
On a group or layer, every visible member sweeps about one axis, as [the help page](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/create-3d-objects.html)
|
||||
describes for a group. The following rules are VectorCraft's own: type stays editable and sweeps as its glyph outlines,
|
||||
the shared axis lies on the chosen edge of the members' bounds together, and each member keeps its own paint.
|
||||
The faces of all members draw in one back-to-front order. The opacity and blending mode of a member, or of a group
|
||||
inside the group, apply once to all its faces. Where a face of another member lies between its faces in that order
|
||||
and overlaps them on the page, its faces are split there, and the opacity and blending mode apply once to each part.
|
||||
The faces of all members draw in one back-to-front order. The opacity, blending mode and opacity mask of a member,
|
||||
or of a group inside the group, apply once to all its faces. Where a face of another member lies between its faces
|
||||
in that order and overlaps them on the page, its faces are split there, and the opacity, blending mode and opacity
|
||||
mask apply once to each part. The mask stays where it is on the page; it does not turn with the faces.
|
||||
Two faces overlap when the convex hulls of their outlines come within the 0.75 pt seam stroke of each other. After
|
||||
4,194,304 overlap tests on one group, each further face of such a member is drawn as a part of its own.
|
||||
The profile's horizontal distance from a vertical axis is its radius. **Axis side** chooses the left or right
|
||||
|
||||
@@ -658,6 +658,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- **Reordering document tabs (G7, #5663):** a document tab dragged along the tab strip moves its document among the others ([public source](https://helpx.adobe.com/illustrator/using/customizing-workspace.html)); a line marks the gap it goes in. The current document stays current, each document keeps its own zoom, centre and artboard, the views Window › New Window opened move with their document, and Window › Next/Previous Document follow the new order. `document.move` (`{from?, to}`) does the same from MCP, the control channel and recordings. `crates/ui-egui/src/tests/tab_reorder.rs`, `crates/engine/src/tests/document_move.rs`.
|
||||
- **Preferences › User Interface › Open Documents As Tabs (G7, #394):** checked by default, documents open under tabs in one document window ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/workspace-overview.html)). With the box cleared, each document opened or created afterwards opens in a floating window of its own, at the next cascade place, as Float in Window floats it; documents already open stay where they are, and checking the box again leaves floating documents in their windows. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
|
||||
- **Preferences search:** a Search Preferences field above the category list has the keyboard focus when the dialog opens, and Cmd/Ctrl+F gives it back; typing lists the settings it finds, and choosing one shows its category, as the [help page](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/set-app-preferences.html) describes. VectorCraft's own rules: the field matches a setting's label, section or category, ignoring case, in the interface language or in English; the list replaces the categories while the field has text and shows every match, or "No settings found"; the chosen setting's row is highlighted and scrolled into view; Escape in the field with text clears it, and Escape in the empty field closes the dialog. The dialog's `__search` field (`ui.dialog.set`) holds the text. Tests: `crates/ui-egui/src/prefs_dialog/tests.rs`.
|
||||
- **Reset Preferences asks first (#6435):** the Preferences dialog's Reset Preferences button opens a question over the dialog (`resetPreferences`). Cancel or Escape goes back to Preferences unchanged, with its category, search and edits kept; OK goes back with every preference at its default value, which OK in Preferences applies and Cancel there discards. The question and its wording are VectorCraft's own. Tests: `crates/ui-egui/src/prefs_dialog/tests.rs`.
|
||||
- **Window › New Window (G7, #4116):** New Window (`window.newWindow`) opens another view of the current document ([public source](https://helpx.adobe.com/illustrator/using/viewing-artwork.html) describes showing one document in several windows at once), with its own zoom, center, rotation, artboard and Outline or Preview, as a tab of its own; an edit in one view shows in every view. Tile, Cascade, Float in Window, Float All in Windows and Consolidate All Windows arrange each view as they arrange a document, and Next and Previous Document step through the views. Closing a view leaves the document open; closing its last view closes the document, asking to save it first. The view's number after the name in its tab ("Name:2", while the document has more than one view) is VectorCraft's own. Tests: `crates/ui-egui/src/doc_windows/tests.rs`.
|
||||
|
||||
- **Document Setup modal handoff:** Document Setup → Edit Artboards applies valid settings, closes the dialog and activates the Artboard tool. Pointer-click tests cover successful handoff, invalid settings, OK, Cancel and Undo in `dialogs/document_setup.rs`.
|
||||
@@ -923,6 +924,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
While a spiral is dragged out, Alt adds or removes whole winds of four segments and Ctrl/Cmd changes the decay. The spiral's inner end keeps the distance from the center it had on the first modified drag event, while the outer end follows the pointer. The [default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) (archived copy) list Alt-drag to add or subtract winds and Ctrl-drag to change the decay rate; [Draw spirals](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-spirals.html) defines four segments per full wind and Decay as the amount each wind decreases. An 80% spiral has 80% of its outer radius after four segments; newly drawn spirals with the same settings wind more broadly than in earlier VectorCraft builds. Rounding the fitted count to whole winds from the count on the first Alt-only drag event, the held inner radius, the ranges (2…1000 segments, 5…99.99 % decay), Ctrl/Cmd taking precedence when both are held, and the new values staying as Spiral tool options are our own rules. Switching back to Alt uses the current count as its new base while keeping the held inner end.
|
||||
Escape while an arc, spiral or grid is dragged out cancels it and puts back the options its keys, Alt and Ctrl/Cmd changed (slope, open or closed, base axis, segments, decay, dividers and skews), so the next drag and the tool's options start from the values before the canceled drag; a drag that is let go keeps them, and an option set with `tool.setOption` during the drag stays. This is VectorCraft's own rule.
|
||||
- **Cutting:** Eraser, Scissors, Knife, Mirror & Cut, Line Cut and Rectangle Cut (real geometry, compound paths keep their holes).
|
||||
A Scissors cut (`path.split`) leaves one of the two new end points selected and the rest of the path unselected, as [Cut paths](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/split-paths.html) and the "Split a path" section of the [CS3 user guide](https://help.adobe.com/archive/en_US/illustrator/cs3/illustrator_cs3_help.pdf) describe: the two end points lie on top of each other, and a Direct Selection drag from them moves the selected one with its handles and no other anchor. A closed path cut once stays one open path.
|
||||
While Alt is held, the Scissors tool works as the Add Anchor Point tool ([Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)): a click on a segment adds an anchor point without cutting (`path.insertAnchor`), and the pointer shows the Add Anchor Point cursor. When Alt is let go, a click cuts again.
|
||||
Alt-drag with the Knife cuts in a straight line, and Shift+Alt-drag cuts at 45° or 90°, as [Cut objects using the Knife and Scissors tools](https://web.archive.org/web/20260926095638/https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/cut-objects.html) and [Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) describe. Alt counts only when held at the press; the dashed line runs from the press point to the pointer, stays straight after Alt is let go, and release sends `path.knife` with those two points. Alt pressed after the press leaves the cut freehand. Shift+Alt keeps the endpoint at 45° steps from the horizontal and vertical, whatever the Constrain Angle.
|
||||
With Smart Guides on, a straight Knife cut snaps to anchor points and paths: [Cut objects using the Knife and Scissors tools](https://helpx.adobe.com/illustrator/desktop/manage-objects/edit-objects/cut-objects.html) describes the Knife snapping to anchor points and paths while Smart Guides are on. Its ends snap as the ends of the Pencil's straight line do, to anchor points and path segments of art that is shown: the press, and the release of an Alt-drag. Shift+Alt steps 45° from the snapped press, and its release stays on that step. With Smart Guides off, or with Snap to Pixel or Snap to Grid on, the cut is not snapped. The dashed line and `path.knife` use the snapped points.
|
||||
@@ -1063,6 +1065,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- Extrude & Bevel, Inflate and Rotate apply to type: it makes a solid of its glyph outlines, painted with its own fills and strokes or, when it has none, its characters' colour (the first run's), and stays type, so editing the text reshapes the solid (#4715).
|
||||
- Extrude & Bevel, Inflate and Rotate (Effect › 3D and Materials): live effects in the `three-d` crate with depth, twist, taper, solid or hollow caps, five original bevel shapes (width, height, repeat, spacing, inside, both sides), Inflate's volume, both sides, twist, taper and cap, Rotate's turned fills and strokes, Position presets, the rotation and light gizmos, SVG/PDF vector output and Expand Appearance; one 3D effect per object; covered by geometry, effect, canvas-versus-export, command and dialog tests ([three-d.md](three-d.md)).
|
||||
- Extrude & Bevel and Inflate build detailed shapes: each solid takes up to 65,536 outline points and 163,840 faces (VectorCraft's own limits, was 2,048 points and 32,768 faces), counted before any point is sampled; when the faces do not fit, every curve takes fewer pieces, down to 4. A closed path of 2,000 curves extrudes with a bevel and inflates, and paths of 5,000 and 10,000 curves build at the default settings (`crates/three-d/src/solid/tests_limits.rs`).
|
||||
- Extrude & Bevel (both kinds) and Rotate keep the art's curves in the faces they draw, export and expand: caps and the edges of walls and bevels where they meet a cap are cubics with smooth anchors where the art's are, fitted within 0.001 pt in perspective and within the sampling tolerance on a bevel's inset. An edge of a twisted wall that is cut into pieces, a piece of curve that does not fit after 8 halvings, and the curves past 1,048,576 for one solid stay straight (VectorCraft's own values; [three-d.md](three-d.md#what-differs-from-the-public-documentation)). Tests `solid::tests::a_cap_keeps_the_curves_of_the_art`, `a_cap_in_perspective_fits_the_projected_curves`, `a_beveled_cap_and_its_walls_share_their_curves`, `twisted_beveled_tiny_and_degenerate_caps_keep_their_curves`.
|
||||
- Export 3D Object (Asset Export's menu): an object with Extrude & Bevel, Revolve, Inflate or Rotate becomes a 3D asset whose Format is USDA (the default), USDZ, glTF or OBJ, and Export writes each of its solids as a mesh with a material in its paint's color (`assets.add3D`, `assets.set3DFormat`, `assets.export3D`; [export 3D vector artwork](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/export-3d-vector-artwork.html)).
|
||||
- Initial live Revolve (#605, #846): a portable geometry and shading crate (`three-d`), editable path and compound profiles, compact options with artboard rotation and light gizmos, SVG/PDF vector output and Expand Appearance that can trim covered faces. Open ends have continuous caps by default, shading is smooth with one to ten colored lights, and intersecting surfaces' visibility is approximate ([revolve.md](revolve.md)).
|
||||
- Revolve takes type, as its glyph outlines, and groups or layers, whose members sweep about one axis as [the help page](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/create-3d-graphics/create-3d-objects.html) describes; a surface can have up to 16,384 profile points, and curves take fewer samples when a surface would pass 163,840 faces (#3617).
|
||||
@@ -1347,7 +1350,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
Invisible text in a PDF (rendering mode 3, which carries a scanned page's recognized text over the scan) opens as type with no fill or stroke, a run of its own beside visible text on the same line; text that can't come in as type (opened as outlines, Type 3 fonts) is left out with a warning. PDF export, and printing, write characters with no fill or stroke as real text in rendering mode 3, so they draw nothing but stay selectable and searchable, also in PDF/X-1a and PDF/A-2b (#2675, `pdf/src/tests/import_fidelity.rs`, `pdf/src/tests/realtext.rs`).
|
||||
A PDF, or a `.ai` read through its PDF part, whose spot inks have the same values but different names opens with a spot swatch for each ink. The solid fills and strokes of paths and type, and the stops of gradients on paths, link to the swatch of the ink they are painted in, the points of a gradient mesh in one of these inks keep the ink's color, and PDF export writes each of these inks as its own separation (`pdf/src/tests/import_color.rs`). Paint in these inks inside pattern cells, soft masks and type 3 glyphs, and gradients on type in these inks, still import as RGB. Type in one of these inks can also import as RGB when type in another Separation or DeviceN ink is shown with no path drawn between them. The stops of an axial or radial shading in a DeviceN space that mixes one spot ink with process plates (a spot ink over a constant cyan, for example) link to the ink's swatch at its tint and keep the process plates, and PDF and EPS (Level 3) export write those stops in a DeviceN space of the ink and those plates, so the spot plate is not lost (#3235, `import_color::a_device_n_gradient_of_a_spot_ink_over_a_process_plate_keeps_both_inks`). Solid fills that mix a spot ink with process plates, and stops that mix several spot inks, still import as RGB, with the spot inks kept as swatches.
|
||||
A `.ai` or EPS opened from its editing data keeps both swatches when a pattern's name, or a named color's, is also another swatch's: a pattern's swatch named as a default swatch, a named color or a palette color becomes "Name 2", and a named color named as a gradient or pattern swatch of the palette becomes "Name 2", with the fills, strokes and gradient stops linked to it following it (`ai::tests::a_pattern_swatch_named_as_another_swatch_is_numbered`, `ai::tests::a_color_named_as_a_palette_gradient_or_pattern_is_numbered`).
|
||||
PDF export writes a linear or radial gradient that mixes a spot color with process colors, or two spot colors, as a DeviceN shading of the spot inks and the process inks its stops use, so each spot ink prints on its own plate; an off-center midpoint is written as a stop with the inks halfway between its two stops. A gradient that mixes a spot color with RGB or Lab colors is still written without the spot ink, with a warning. A Perceptual gradient of spot inks is written as a Separation or DeviceN shading of the stops on its curve; a dithered gradient fill, and a Perceptual gradient with RGB or Lab stops other than white or more than 4,096 stops on its curve, are written as images in process colors, with a warning. A Lab spot color in such a shading is named by the same Separation space, with the Lab alternate, as its flat fills (`engine/src/tests/linked_stops.rs`, `engine/src/tests/labspots.rs`).
|
||||
PDF export writes a linear or radial gradient that mixes a spot color with process colors, or two spot colors, as a DeviceN shading of the spot inks and the process inks its stops use, so each spot ink prints on its own plate; an off-center midpoint is written as a stop with the inks halfway between its two stops. A gradient that mixes a spot color with RGB or Lab colors is still written without the spot ink, with a warning. A CMYK document, or an export that converts colors to a CMYK destination profile, writes RGB and Lab stops in CMYK first (unless the export converts colors to an RGB destination profile), so there such a gradient, Classic or Perceptual, is written as a DeviceN shading that keeps the spot ink. A Perceptual gradient of spot inks is written as a Separation or DeviceN shading of the stops on its curve; a dithered gradient fill, and a Perceptual gradient with RGB or Lab stops other than white or more than 4,096 stops on its curve, are written as images in process colors, with a warning. A Lab spot color in such a shading is named by the same Separation space, with the Lab alternate, as its flat fills (`engine/src/tests/linked_stops.rs`, `engine/src/tests/labspots.rs`).
|
||||
PDF export writes a spot color named All or None (in a Separation space, the names of every plate and of no plate: [ISO 32000-1:2008](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf), 8.6.6.4) as the ink All Spot or None Spot (All Spot 2 and on when a spot swatch already has that name) in its fills, strokes, gradients, colorized images and Lab alternates; Registration still prints on every plate (`pdf/src/tests/output.rs`).
|
||||
- `.vectorcraft` (lossless JSON, compressed, atomic saves, save down to v1), Save As with a format chooser, Save a Copy, Revert, templates, Data Recovery after a crash (browser tabs hold their copies with Web Locks too, so a background tab's copies are never taken for a crash's, and a tab repairs copies another removed when it resumes: #367), background save and export. The desktop app writes its warnings and errors (graphics adapter, a lost graphics device, panics the guard recovered from) to standard error and to `logs/vectorcraft.log` next to the preferences, the two previous runs kept and each file capped at 16 MiB, so a launch without a terminal still leaves something to attach to a bug report (#465). Native documents with a lot of path data (traced or generated art, in layers, symbols or patterns) are saved as compact JSON, about a fifth of the size; small documents stay pretty-printed (#1492). Saving to a network drive on macOS falls back to a plain fsync when the share refuses a full flush (#1625, part of #1620; not confirmed on a real share), and a save still succeeds when a folder refuses the temporary file, the rename over the document or copying permissions: the content is written elsewhere first and copied in, and a failed copy keeps the temporary file and names it (#1649, part of #1620). A document opened by a relative path finds its relatively linked images, and `vectorcraft-cli convert` lists what reading the input left out as `importWarnings` (#1606, fixes #1604).
|
||||
Preferences › File Handling › Data Recovery › Folder has a Choose… button on the desktop that picks the folder Data Recovery keeps its copies in ([Illustrator reference, Set Data Recovery preferences](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf)); a picked folder that Data Recovery cannot create its folders in or list is refused with a status message, and the field keeps its value. OK applies the same check to a folder typed into the field (VectorCraft's own rule): when it fails, the dialog stays open, the status bar says why and no preference changes; an empty field (the default folder) and the folder already saved are not checked. Preferences › Plug-ins & Scratch Disks is where additional plug-ins folders are set ([Illustrator preferences help](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/set-app-preferences.html)); its Additional Plug-ins Folder has a Choose… button on the desktop as well, VectorCraft's own, matching Data Recovery's, and VectorCraft's own rule refuses a picked or typed folder whose contents can't be listed. Additional Fonts Folder and Templates Folder stay typed only.
|
||||
@@ -1535,7 +1538,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
An Alt-drag of objects in perspective with the Perspective Selection tool, and an Alt-drag of a plane widget with that tool or the Perspective Grid tool, copy the objects on the first move, and the moves after it move those copies (#1429).
|
||||
With the Perspective Selection tool, Ctrl+Alt-drag (Command+Option-drag on macOS) also copies objects on their perspective plane; Alt/Option-drag continues to do so.
|
||||
With the Perspective Selection tool, holding Shift while moving or copying constrains each object's displacement to the nearest axis or 45° diagonal of its own plane; releasing Shift resumes the raw pointer position, and 5 still moves perpendicular to the plane. Snap to Grid follows the constrained line on the first object's plane and depth.
|
||||
With Snap to Grid on, a move of objects on one perspective plane at different depths moves them all by the same offset on the plane, so they keep their spacing, and the edge of their joint bounds nearer a gridline lands on it. Regression: `snap_moves_objects_at_different_depths_by_one_plane_offset` in `crates/engine/src/tests/persp_select.rs`.
|
||||
With Snap to Grid on, a move of objects on one perspective plane at different depths moves them all by the same offset on the plane, so they keep their spacing. Without Shift, on each axis, the edge of their joint bounds nearer a gridline lands on it when that edge ends within a quarter cell of the gridline. With Shift, an edge also lands only when it ends within a quarter cell of a gridline: a horizontal or vertical move can land only an edge on its own axis, and a 45° move lands the edge on the axis whose nonzero correction is smaller. When no edge on those axes is that close, the move does not snap. A move perpendicular to the plane does not snap. Regression: `snap_moves_objects_at_different_depths_by_one_plane_offset` in `crates/engine/src/tests/persp_select.rs`.
|
||||
When the desktop app opens a document on a thread of its own (#1245), that thread also reads the document's linked files and lays out its type, and the UI thread only adds the finished document: in release builds, the step left on the UI thread takes 0.02 ms instead of 1.04 s for a document with 12 linked 3000 × 2000 TIFFs, and it takes 0.1 ms instead of 0.85 s for one with 300 Auto Size area type objects, every second one in three columns.
|
||||
Laying out type keeps each font face's shaping plans, at most 32 per face, so type objects of the same font, script, direction, language and features share one plan: the layouts of a document of 5,000 short point type objects take 59 ms instead of 73 ms in a release build, and the glyphs, bounds and baselines do not change.
|
||||
Laying out type looks up the font face of each style run once and uses it for the run's glyphs and for the line metrics of a paragraph that starts in that run: the layouts of a document of 5,000 short point type objects take a median of 215 million CPU cycles instead of 236 million in a release build (11 alternating runs of each build on Windows 11, measured while other builds were running), and the glyphs, bounds and baselines do not change.
|
||||
@@ -1693,7 +1696,7 @@ applies to fills). Tests: `color::gradient::tests` (perceptual midpoint, expansi
|
||||
`pdf::tests::dithered_perceptual_gradients_export_as_images_with_noise_on_the_fill` and the Gradient panel's
|
||||
`the_method_menu_makes_the_gradient_perceptual`. Scores and hours unchanged.
|
||||
|
||||
PDF export writes a Perceptual linear or radial gradient whose stops are tints of one spot color (and paper white) as a Separation shading in that ink instead of an image, so the ink keeps its plate ([swatches](https://helpx.adobe.com/illustrator/using/using-creating-swatches.html); [separations](https://helpx.adobe.com/illustrator/using/printing-color-separations.html)). Its tints follow the curve's stops, each the tint with the OKLab lightness of the color there (VectorCraft's own rule). A Perceptual gradient that mixes spot colors with each other or with CMYK or gray stops is written as a DeviceN shading of its inks: at each stop on the curve, the inks of the two stops on either side are mixed at the weight where the two colors mixed in display RGB have the OKLab lightness of the color there, or at the stop's mix position between the two colors when that mix's lightness turns back on the way from one color to the other, as from red to blue (VectorCraft's own rule). Dithered fills, freeform gradients, Perceptual gradients that mix a spot color with RGB or Lab stops, and Perceptual gradients whose curve has more than 4,096 stops once expanded (VectorCraft's own limit) are still images, and the export warns that their spot colors are written in process colors. Tests: `color::gradient::tests::ink_tints_follow_the_perceptual_curve`, `ink_mixes_follow_the_perceptual_curve` and `ink_mixes_use_the_mix_position_when_the_lightness_turns_back`, `engine::tests::linked_stops::perceptual_spot_gradients_export_as_separation_shadings`, `perceptual_spot_gradients_mixed_with_other_inks_export_as_device_n_shadings` and `spot_gradients_drawn_as_images_warn`.
|
||||
PDF export writes a Perceptual linear or radial gradient whose stops are tints of one spot color (and paper white) as a Separation shading in that ink instead of an image, so the ink keeps its plate ([swatches](https://helpx.adobe.com/illustrator/using/using-creating-swatches.html); [separations](https://helpx.adobe.com/illustrator/using/printing-color-separations.html)). Its tints follow the curve's stops, each the tint with the OKLab lightness of the color there (VectorCraft's own rule). A Perceptual gradient that mixes spot colors with each other or with CMYK or gray stops is written as a DeviceN shading of its inks: at each stop on the curve, the inks of the two stops on either side are mixed at the weight where the two colors mixed in display RGB have the OKLab lightness of the color there, or at the stop's mix position between the two colors when that mix's lightness turns back on the way from one color to the other, as from red to blue (VectorCraft's own rule). Dithered fills, freeform gradients, Perceptual gradients that mix a spot color with RGB or Lab stops, and Perceptual gradients whose curve has more than 4,096 stops once expanded (VectorCraft's own limit) are still images, and the export warns that their spot colors are written in process colors. In a CMYK document, or an export that converts colors to a CMYK destination profile, RGB and Lab stops are written in CMYK, so a Perceptual gradient that mixes a spot color with them is a DeviceN shading; an export that converts colors to an RGB destination profile keeps them in RGB. Tests: `color::gradient::tests::ink_tints_follow_the_perceptual_curve`, `ink_mixes_follow_the_perceptual_curve` and `ink_mixes_use_the_mix_position_when_the_lightness_turns_back`, `engine::tests::linked_stops::perceptual_spot_gradients_export_as_separation_shadings`, `perceptual_spot_gradients_mixed_with_other_inks_export_as_device_n_shadings`, `perceptual_spot_gradients_with_rgb_stops_in_a_cmyk_document_export_as_device_n_shadings` and `spot_gradients_drawn_as_images_warn`.
|
||||
|
||||
In these Separation and DeviceN shadings, a white stop linked to a swatch that is not a spot color (a global White swatch, for example) is paper white, 0% of the ink, as an unlinked white stop is, so a gradient of one spot ink and such a stop stays a Separation shading; a stop linked to a white spot color stays a stop of that ink (VectorCraft's own rule). Test: `engine::tests::linked_stops::white_stops_linked_to_process_swatches_are_paper_in_spot_gradients`.
|
||||
|
||||
|
||||
+15
-1
@@ -122,7 +122,11 @@ them all. The members stay editable: moving or editing one reshapes the solids.
|
||||
- Each member's own geometry effects shape it first, then those of each group inside the 3D group that holds it,
|
||||
applied to that group's members together; the 3D group's geometry effects before the 3D effect reshape the
|
||||
members, and those after it bend the result as a whole. The members' other effects, and the group's own fills and
|
||||
strokes, are not drawn. A member's opacity applies to its solids; its blending mode does not.
|
||||
strokes, are not drawn. A member's opacity applies to its solids; its blending mode does not. An opacity mask on
|
||||
a member masks its solids, and one on a group inside the 3D group masks the solids of that group's members
|
||||
together. The mask stays where it is on the page; it does not turn with the solids. A disabled mask is ignored.
|
||||
- When a member, or a group inside the 3D group, has an opacity mask, each solid is drawn on its own, the backmost
|
||||
member's first, as when a solid is transparent.
|
||||
- When every solid is opaque, all members' faces are drawn in one back-to-front order; otherwise each solid is drawn
|
||||
on its own, the backmost member's first.
|
||||
- Hidden members and guides are left out. A group holding a live object other than a blend (an envelope, a mesh, a
|
||||
@@ -277,6 +281,16 @@ out, perspective). No Adobe software, assets, presets or bevel shapes were used.
|
||||
builds them, against the same 163,840. The point limit is counted on the shape as drawn and, when its contours
|
||||
meet, again on the merged outline, which can have more points (bars crossing in a grid trace every hole between
|
||||
them); past it, the shape is too complex for the effect.
|
||||
- The faces drawn, exported and made by **Object › Expand Appearance** keep the shape's curves: the caps of
|
||||
Extrude & Bevel, the flat face of Rotate, and the edges of the walls and bevels where they meet a cap are cubic
|
||||
curves, with smooth anchors where the art's are smooth, while visibility and depth are worked out on the
|
||||
sampled points. Without perspective and bevel each curve maps exactly. With perspective, a curve is split into
|
||||
cubics until each lies within 0.001 pt of the projected outline. A face inset or outset by a bevel follows its
|
||||
sampled outline within the sampling tolerance above, one curve for each sampled edge through its two sampled
|
||||
points, so the cap and the walls next to it draw the edge they share as the same curves. The edges between two bevel steps or two wall slices, a
|
||||
piece of curve that does not fit after 8 halvings, an edge of a twisted wall cut into pieces, and the curves
|
||||
past 1,048,576 for one solid stay straight. The tolerances and limits are VectorCraft's own values. Inflate's
|
||||
faces are polygons.
|
||||
- Inflate also limits the work of filling its grid, which grows with the outline's points times the grid cells its
|
||||
outline crosses. An outline of tens of thousands of points that crosses nearly every cell (a comb of 4,000 teeth
|
||||
across the shape) is refused because the 3D surface is too complex; a closed path of 65,536 points round a
|
||||
|
||||
Reference in New Issue
Block a user