feat(image-editor): Pen tool — vector paths with anchors and bezier handles#46
Merged
feat(image-editor): Pen tool — vector paths with anchors and bezier handles#46
Conversation
…ier handles PS-aligned Pen tool, end-to-end: - **Click** adds a corner anchor (no handles → straight segment to it). - **Click + drag** turns the new anchor into a smooth one with symmetric handles (the drag delta from the anchor sets `hout`; `hin` mirrors). - **Click near the first anchor** (within 8 px, ≥2 anchors total) closes the path. - **Enter** commits the current path open. - **Esc** cancels the in-progress path. The committed result is a new annotation layer with a `path` shape, move-only (per-anchor editing arrives in a follow-up). Both open and closed paths render via `bezierCurveTo` / `quadraticCurveTo` / `lineTo` based on which handles each segment has on either side; closed paths can be filled (the type supports it; v1 UI exposes stroke only — fill is a future OptionsBar bump). State + render layer: - types.ts: new `PathAnchor` (with relative `hin` / `hout` offsets) and `PathShape` join the Shape union. Persists through .json round-trip via the existing tagged-variant serialization. - drawing.ts: `drawPath` walks anchors emitting bezier / quadratic / line segments; closed paths get an additional last→first segment. - hit.ts: bbox covers anchor positions plus handle endpoints (rough bound — exact bezier extents are non-trivial; this is fine for layer picking). No corner handles in v1 — selection chrome shows the bbox. - transform.ts: translate shifts anchor positions; relative handle offsets are unaffected. - LayersPanel.tsx: layerLabelKey gains a `path` branch. Canvas integration: - New `pen-drawing` interaction kind (anchors / pressed / cursor). - mousedown handles close-vs-append; sets `pressed=true`. - mousemove with `pressed` updates the last anchor's symmetric handles from the drag delta. Without press, just tracks cursor for the rubber-band preview. - mouseup releases `pressed`; anchors stay until close / Enter / Esc. - `CanvasHandle` gains `hasPendingPen` / `commitPendingPen` / `cancelPendingPen`, mirroring the existing crop pair so Enter / Esc in the parent's keyboard handler route to the right action. - `drawPenPreview` overlay: orange first-anchor square (close target), white squares for the rest; blue handle tethers + dots; dashed rubber-band line / curve from the last anchor to the cursor. Other wiring: - ToolsPalette: pen entry no longer carries `stub: true`. - tool-meta: `pen` removed from STUB_TOOLS; only the three sample- pixel tools (spotHeal / stamp / historyBrush) remain. - OptionsBar: pen variant — stroke width + colour + the same hint text the keyboard handler honours. - i18n: `penHint` (en + zh-CN) and `annoLabel.path`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PS-aligned Pen tool, end-to-end:
Result is a new annotation layer with a `path` shape — open paths render as a stroked curve; closed paths additionally support fill (the type carries `fill?`; the v1 OptionsBar exposes stroke + colour only — the fill swatch is a follow-up).
State + render layer
Canvas integration
Other wiring
Built on top of PR #45
This branch was cut from `feat/image-editor-note-frame-pathsel` (PR #45) since both tools touch the same files. Once #45 merges, this PR will be a clean fast-forward; if any conflicts arise on rebase, they'll be in the shape switch + STUB_TOOLS — easy to resolve.
Test plan
Stub palette status after this PR
Final PR D (sample-pixel family — Spot Heal + Clone Stamp + History Brush) closes out the palette.
🤖 Generated with Claude Code