M4: shape builder tool (Shift+M): merge or remove the regions of overlapping shapes (#39)

The selected shapes' outlines split the canvas into regions, computed as the faces of their
arrangement with linesweeper (curves kept, per-shape fill rules, holes and islands handled) and
cached per document version and selection.

- Engine: shapeBuilder.regions / merge / remove / mode commands, ToolId::ShapeBuilder and
  tool.shapeBuilder (catalog: Tools > Shape builder, Shift+M). Merge makes one vector from the
  picked regions styled like the top-most shape over the first; every shape covering a picked
  region loses that area (becomes a vector, or is deleted when nothing is left). One gesture or
  command is one undo step. Session::tool_key routes Enter (finish) and vector edit keys.
- Pathops: faces() (arrangement faces with their covering inputs) and union_faces().
- UI: region edges on the canvas, hovered/collected regions hatched (danger colour to remove),
  no transform box while building, a toolbar pill with Merge/Remove modes, a hint and Done; shared
  pill/tool-cell/divider/Done helpers; original merge/remove icons drawn in code.
- Tests: pathops faces; engine regions, merge, remove, curves, groups, rotation, pointer
  click/drag/Alt, modes, Escape/Enter, cache, junk parameters and degenerate geometry.
- Docs: ROADMAP, README, docs/mcp.md, docs/control-protocol.md, docs/parity.md.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
BFlat
2026-10-10 05:55:12 +07:00
committed by GitHub
co-authored by Claude Opus 5.5
parent e360b6fa52
commit 476cdd95f1
18 changed files with 1154 additions and 83 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ A mouse gesture with the current tool. `kind`: `down`, `move`, `up`, `click`, `d
### `ui.key {key, shift?, alt?, cmd?, ctrl?}`
A key as if typed on the canvas (in vector edit mode the edit keys come first: `V` `P` `Q` `X` `Shift+B` tools, arrows nudge points, `Delete`/`Shift+Delete`, `Cmd+A`, `Cmd+J`): a menu shortcut (`{"key":"G","cmd":true}` groups), a tool key,
A key as if typed on the canvas (in vector edit mode the edit keys come first: `V` `P` `Q` `X` `Shift+B` tools, arrows nudge points, `Delete`/`Shift+Delete`, `Cmd+A`, `Cmd+J`; with the shape builder, `Enter` finishes): a menu shortcut (`{"key":"G","cmd":true}` groups), a tool key,
`Escape`, `Delete`/`Backspace`, `Enter`, arrows (nudge; shift = big nudge; auto layout children move one place along the flow; with `cmd` they resize the selection). While text is being
edited, keys go to the text editor. Returns `{"command": id}` for shortcuts.
+17
View File
@@ -155,6 +155,23 @@ read library files return an error).
- **Update**: `library.updates` lists the components and styles with a newer published version;
`library.acceptUpdates {ids? (library ids)}` pulls them in, keeping instance overrides.
### Shape builder
The selected shapes' outlines split the canvas into regions (areas covered by the same shapes).
`shapeBuilder.regions {ids?}` lists them: an SVG `path` in page coordinates, the `sources` covering
each one (top-most first), `bounds` and a `point` inside. `shapeBuilder.merge {ids?, regions?,
points?}` makes one vector from the regions (by index, or the regions under page points), styled
like the top-most shape over the first one; `shapeBuilder.remove {ids?, regions?, points?}` deletes
them. Either way every shape covering a picked region loses that area (it becomes a vector, or goes
when nothing is left), and each call is one undo step. The tool is `tool.shapeBuilder` (Shift+M):
click or drag across regions with `pointer` to merge, add `alt` to remove; `shapeBuilder.mode
{mode: merge|remove}` swaps what a plain click does.
```json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"run_command","arguments":{"command":"shapeBuilder.regions","params":{"ids":[2,3]}}}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"run_command","arguments":{"command":"shapeBuilder.merge","params":{"ids":[2,3],"points":[[120,80],[160,80]]}}}}
```
### Importing REST API file JSON
The JSON returned by the public REST API's file endpoint (`GET /v1/files/:key`, best with
+2 -2
View File
@@ -1,8 +1,8 @@
# Parity report
Generated by `cargo xtask parity`: FigCraft commands registered for the incumbent's main menu and toolbar (340 commands in the engine).
Generated by `cargo xtask parity`: FigCraft commands registered for the incumbent's main menu and toolbar (357 commands in the engine).
**194 / 200 menu items (97.0%)**
**195 / 201 menu items (97.0%)**
## Missing