Editor has no 'reverse path direction' or 'close path' operations — common path ops in any vector editor are missing #209

Closed
opened 2026-10-04 00:00:48 +00:00 by Mark · 1 comment
Owner

Finding

grep -n 'reversePath\|REVERSE_PATH\|closePath\|CLOSE_PATH\|isClosed' src/ returns 0 hits. The Editor has:

  • Split (scissors tool, per-segment cut)
  • Add node
  • Delete node
  • Convert node type

But no:

  • Reverse path direction: nodes 0,1,2,3 → 3,2,1,0. Critical for changing the order stitches are produced; matters when one end of the path is at the thread spool and the user wants the other end to start.
  • Close path: a path is "open" if its first and last nodes are different; "closed" if they're at the same position. Closing a path loops it (the last segment connects back to the first). This affects fill rendering: an open path can be filled, but closed paths fill more cleanly with no overlap at the join.

Both are standard operations in any vector editor. The StitchPath type has no isClosed field; either the first node is implicitly the close point, or the path is always open.

src/components/editor/types.ts:

  • StitchPath has nodes: PathNode[], stitchType, density, hex, visible
  • No closed field

Where

  • Types: src/components/editor/types.ts
  • Right panel: src/components/editor/EditorRightPanel.tsx (no reverse/close buttons)
  • No reducer: grep -n 'REVERSE_PATH\|CLOSE_PATH' src/components/editor/ returns 0

Suggested fix shape

Reverse path:

<button onClick={onReverseSelectedPath} title="Reverse path direction">
  <ArrowRightLeft size={12} />
  Reverse
</button>

onReverseSelectedPath reverses the nodes array in the selected path. The control points (controlIn / controlOut) need to be swapped too: a node's incoming handle becomes its outgoing handle and vice versa. Pushing history for undo.

Close path:

Add a closed: boolean field to StitchPath and a toggle in the right panel:

<label>
  <input
    type="checkbox"
    checked={selectedPath.closed ?? false}
    onChange={(e) => onTogglePathClosed(e.target.checked)}
  />
  Close path
</label>

The canvas renderer uses the closed flag to draw the closing segment (no node is added; the renderer just draws a line from last → first). Fill rendering respects the flag.

Severity

Medium. These are table-stakes operations for any vector editor; power users notice their absence immediately.

Discovered by

Gap analysis (2026-10-02) of editor path operations.

  • #204 (open) — Editor split/merge. Same family.
  • #205 (open) — Editor mirror/rotate. Same family.
  • #187 (open) — Editor duplicate. Same family.
  • #188 (open) — Editor multi-path selection. Bulk close/reverse would be a follow-up.
## Finding `grep -n 'reversePath\|REVERSE_PATH\|closePath\|CLOSE_PATH\|isClosed' src/` returns 0 hits. The Editor has: - Split (scissors tool, per-segment cut) - Add node - Delete node - Convert node type But no: - **Reverse path direction**: nodes 0,1,2,3 → 3,2,1,0. Critical for changing the order stitches are produced; matters when one end of the path is at the thread spool and the user wants the other end to start. - **Close path**: a path is "open" if its first and last nodes are different; "closed" if they're at the same position. Closing a path loops it (the last segment connects back to the first). This affects fill rendering: an open path can be filled, but closed paths fill more cleanly with no overlap at the join. Both are standard operations in any vector editor. The StitchPath type has no `isClosed` field; either the first node is implicitly the close point, or the path is always open. `src/components/editor/types.ts`: - `StitchPath` has `nodes: PathNode[]`, `stitchType`, `density`, `hex`, `visible` - No `closed` field ## Where - Types: `src/components/editor/types.ts` - Right panel: `src/components/editor/EditorRightPanel.tsx` (no reverse/close buttons) - No reducer: `grep -n 'REVERSE_PATH\|CLOSE_PATH' src/components/editor/` returns 0 ## Suggested fix shape **Reverse path**: ```tsx <button onClick={onReverseSelectedPath} title="Reverse path direction"> <ArrowRightLeft size={12} /> Reverse </button> ``` `onReverseSelectedPath` reverses the `nodes` array in the selected path. The control points (`controlIn` / `controlOut`) need to be swapped too: a node's incoming handle becomes its outgoing handle and vice versa. Pushing history for undo. **Close path**: Add a `closed: boolean` field to `StitchPath` and a toggle in the right panel: ```tsx <label> <input type="checkbox" checked={selectedPath.closed ?? false} onChange={(e) => onTogglePathClosed(e.target.checked)} /> Close path </label> ``` The canvas renderer uses the `closed` flag to draw the closing segment (no node is added; the renderer just draws a line from last → first). Fill rendering respects the flag. ## Severity Medium. These are table-stakes operations for any vector editor; power users notice their absence immediately. ## Discovered by Gap analysis (2026-10-02) of editor path operations. ## Related - #204 (open) — Editor split/merge. Same family. - #205 (open) — Editor mirror/rotate. Same family. - #187 (open) — Editor duplicate. Same family. - #188 (open) — Editor multi-path selection. Bulk close/reverse would be a follow-up.
Author
Owner

Shipped in commit 45ba490 on origin/main.

  • types.ts: optional closed?: boolean on StitchPath (falsy for older persisted designs).
  • canvasRenderer.ts: closing segment from last node back to first when path.closed (bezier handles honored on the join; respects playback dim).
  • Editor.tsx: handleReverseSelectedPath (reverses nodes + swaps controlIn/controlOut so curve shape survives) + handleTogglePathClosed (flips the flag on every selected path). Both multi-select fan-out, push history, mark dirty.
  • EditorRightPanel.tsx: new Direction & Closure subsection in Path Properties — Reverse button + Closed checkbox. Back-compat: hidden when both callbacks are omitted.

12 new vitest cases (renderer closing-segment variants + right-panel click / disable / back-compat). Full suite 1001/1001, tsc + eslint clean.

Shipped in commit 45ba490 on origin/main. - types.ts: optional `closed?: boolean` on StitchPath (falsy for older persisted designs). - canvasRenderer.ts: closing segment from last node back to first when path.closed (bezier handles honored on the join; respects playback dim). - Editor.tsx: handleReverseSelectedPath (reverses nodes + swaps controlIn/controlOut so curve shape survives) + handleTogglePathClosed (flips the flag on every selected path). Both multi-select fan-out, push history, mark dirty. - EditorRightPanel.tsx: new Direction & Closure subsection in Path Properties — Reverse button + Closed checkbox. Back-compat: hidden when both callbacks are omitted. 12 new vitest cases (renderer closing-segment variants + right-panel click / disable / back-compat). Full suite 1001/1001, tsc + eslint clean.
Mark closed this issue 2026-10-04 03:23:49 +00:00
Sign in to join this conversation.
No description provided.