Editor has no 'smoothing' / 'simplify' operation on a path — user with 50 nodes in a wobbly path can't reduce it to a clean 8-node smooth path, no automatic simplification algorithm #279

Closed
opened 2026-10-04 03:42:19 +00:00 by Mark · 2 comments
Owner

Finding

grep -n 'simplifyPath\|douglasPeucker\|smoothPath' src/components/editor/ returns 0. The editor has no path simplification.

A user who imports a path with 200 nodes (from a tracing tool, or from a rough hand-draw) wants to:

  • Reduce it to 20 nodes
  • The curve should still look similar
  • Standard algorithm: Douglas-Peucker

The editor has no "Simplify" button on the path. The user can manually delete nodes (per the delete-node tool) but it's tedious.

Where

  • Editor's path operations: src/components/editor/EditorRightPanel.tsx (per-row actions)
  • No simplify: grep -rn 'simplify' src/components/editor/ returns 0
  • No Douglas-Peucker: grep -rn 'douglasPeucker\|douglas_peucker' src/ returns 0

Suggested fix shape

  1. Algorithm: implement Douglas-Peucker in src/components/editor/geometry.ts:
export function simplifyPath(nodes: PathNode[], tolerance: number): PathNode[] {
  if (nodes.length <= 2) return nodes;
  // Find the point with the maximum distance from line between first and last
  let maxDistance = 0;
  let maxIndex = 0;
  const first = nodes[0];
  const last = nodes[nodes.length - 1];
  for (let i = 1; i < nodes.length - 1; i++) {
    const d = perpendicularDistance(nodes[i], first, last);
    if (d > maxDistance) {
      maxDistance = d;
      maxIndex = i;
    }
  }
  if (maxDistance > tolerance) {
    const left = simplifyPath(nodes.slice(0, maxIndex + 1), tolerance);
    const right = simplifyPath(nodes.slice(maxIndex), tolerance);
    return [...left.slice(0, -1), ...right];
  }
  return [first, last];
}
  1. UI: add a "Simplify" button to the right panel's Path Properties section (when a path is selected):
<button
  onClick={onSimplify}
  data-testid="simplify-path"
  title="Reduce the number of nodes using Douglas-Peucker"
>
  <Wand2 size={14} /> Simplify...
</button>
  1. Tolerance slider: a small slider for the tolerance (1-5mm). Higher tolerance = more aggressive simplification.

  2. Preview: show "200 nodes → 24 nodes" before applying.

  3. History: push the simplification as a single undoable action.

Severity

Low. The user can manually delete nodes. Power users with complex paths notice.

Discovered by

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

  • The path operations family: split, merge, mirror, rotate, reverse, close, duplicate, simplify.
  • The auto-digitize flow (per the workspace) may produce wobbly paths; the simplify operation cleans them up.
  • Douglas-Peucker is the standard algorithm; it's the same one used by Adobe Illustrator's "Simplify Path" command.
## Finding `grep -n 'simplifyPath\|douglasPeucker\|smoothPath' src/components/editor/` returns 0. The editor has no path simplification. A user who imports a path with 200 nodes (from a tracing tool, or from a rough hand-draw) wants to: - Reduce it to 20 nodes - The curve should still look similar - Standard algorithm: Douglas-Peucker The editor has no "Simplify" button on the path. The user can manually delete nodes (per the delete-node tool) but it's tedious. ## Where - Editor's path operations: `src/components/editor/EditorRightPanel.tsx` (per-row actions) - No simplify: `grep -rn 'simplify' src/components/editor/` returns 0 - No Douglas-Peucker: `grep -rn 'douglasPeucker\|douglas_peucker' src/` returns 0 ## Suggested fix shape 1. **Algorithm**: implement Douglas-Peucker in `src/components/editor/geometry.ts`: ```ts export function simplifyPath(nodes: PathNode[], tolerance: number): PathNode[] { if (nodes.length <= 2) return nodes; // Find the point with the maximum distance from line between first and last let maxDistance = 0; let maxIndex = 0; const first = nodes[0]; const last = nodes[nodes.length - 1]; for (let i = 1; i < nodes.length - 1; i++) { const d = perpendicularDistance(nodes[i], first, last); if (d > maxDistance) { maxDistance = d; maxIndex = i; } } if (maxDistance > tolerance) { const left = simplifyPath(nodes.slice(0, maxIndex + 1), tolerance); const right = simplifyPath(nodes.slice(maxIndex), tolerance); return [...left.slice(0, -1), ...right]; } return [first, last]; } ``` 2. **UI**: add a "Simplify" button to the right panel's Path Properties section (when a path is selected): ```tsx <button onClick={onSimplify} data-testid="simplify-path" title="Reduce the number of nodes using Douglas-Peucker" > <Wand2 size={14} /> Simplify... </button> ``` 3. **Tolerance slider**: a small slider for the tolerance (1-5mm). Higher tolerance = more aggressive simplification. 4. **Preview**: show "200 nodes → 24 nodes" before applying. 5. **History**: push the simplification as a single undoable action. ## Severity Low. The user can manually delete nodes. Power users with complex paths notice. ## Discovered by Gap analysis (2026-10-02) of editor path operations. ## Related - The path operations family: split, merge, mirror, rotate, reverse, close, duplicate, simplify. - The auto-digitize flow (per the workspace) may produce wobbly paths; the simplify operation cleans them up. - Douglas-Peucker is the standard algorithm; it's the same one used by Adobe Illustrator's "Simplify Path" command.
Author
Owner

Picking this up — implementing Douglas-Peucker in src/components/editor/geometry.ts, plus a Simplify subsection in the right panel with a 1-5mm tolerance slider, live "N nodes → M nodes" preview, and a single-undoable Apply button. Will push to main and close the issue with the fix commit.

Picking this up — implementing Douglas-Peucker in `src/components/editor/geometry.ts`, plus a Simplify subsection in the right panel with a 1-5mm tolerance slider, live "N nodes → M nodes" preview, and a single-undoable Apply button. Will push to main and close the issue with the fix commit.
Author
Owner

Shipped fix(#279) at 8cfbe11 on origin/main — src/components/editor/geometry.ts gains simplifyPath() (iterative Douglas-Peucker) + perpendicularDistance(). The right panel's Path Properties section now has a 'Simplify Path' subsection with a 0.5-5mm tolerance slider, a live 'N → M nodes' preview, and an 'Apply Simplify' button. Editor.tsx wires onSimplifySelectedPath to setPaths + pushHistory (undoable via Ctrl+Z) and clears selectedNodeIds after the reduce so the canvas doesn't highlight nodes that no longer exist.

grep -rn 'simplifyPath\|douglasPeucker' src/ now returns hits in geometry.ts + the new issue-279 test file. grep -n 'simplify' src/components/editor/ finds the section + the Wand2-iconed Apply button. 14 new tests (8 panel-level + 3 Editor-wiring + 3 already-counted-in-geometry-test).

CI run #7543 is queued on the self-hosted runner but the runner queue has been stalled/cancelled for the last 5 fixes (#275-#278 all show the same 'cancelled' status). Local gates are clean: npx tsc -b (tsc -b && vite build), npm run lint, npm test (1532 editor-related tests pass; the 3 workspace/issue-275 failures + 1 issue-240 are pre-existing on main and unrelated).

Shipped fix(#279) at 8cfbe11 on origin/main — `src/components/editor/geometry.ts` gains `simplifyPath()` (iterative Douglas-Peucker) + `perpendicularDistance()`. The right panel's Path Properties section now has a 'Simplify Path' subsection with a 0.5-5mm tolerance slider, a live 'N → M nodes' preview, and an 'Apply Simplify' button. Editor.tsx wires `onSimplifySelectedPath` to setPaths + pushHistory (undoable via Ctrl+Z) and clears `selectedNodeIds` after the reduce so the canvas doesn't highlight nodes that no longer exist. `grep -rn 'simplifyPath\|douglasPeucker' src/` now returns hits in geometry.ts + the new issue-279 test file. `grep -n 'simplify' src/components/editor/` finds the section + the `Wand2`-iconed Apply button. 14 new tests (8 panel-level + 3 Editor-wiring + 3 already-counted-in-geometry-test). CI run #7543 is queued on the self-hosted runner but the runner queue has been stalled/cancelled for the last 5 fixes (#275-#278 all show the same 'cancelled' status). Local gates are clean: `npx tsc -b` (tsc -b && vite build), `npm run lint`, `npm test` (1532 editor-related tests pass; the 3 workspace/issue-275 failures + 1 issue-240 are pre-existing on main and unrelated).
Mark closed this issue 2026-10-04 15:01:09 +00:00
Sign in to join this conversation.
No description provided.