Skip to content

feat(plot): route pen-up travel around the magnets - #75

Merged
BernardJen merged 1 commit into
mainfrom
feat/magnet-avoid
Sep 12, 2026
Merged

BernardJen merged 1 commit into
mainfrom
feat/magnet-avoid

Conversation

@BernardJen

Copy link
Copy Markdown
Contributor

Closes #64.

Why

Knowing where the magnets are was only half of it. orderPolylines picks the next stroke by nearest distance, so on an A0 bed a pen-up leg can cross the whole sheet in one straight line — over a magnet. The carriage strikes it at travel feed, drags the sheet, and the work origin is gone.

#9's overlay could warn about geometry drawn through a magnet, because the operator can fix that by moving something. It could say nothing useful about travel, because the fix for travel isn't to move anything — it's to go around.

How

routeAround pushes a leg's closest approach out past the edge of the first zone it clips, then routes each half the same way, to a bounded depth. The zones are few and far apart, so this needs no general path planner — a visibility graph would be the general answer to a problem this doesn't have. The depth cap exists for the degenerate input, where unbounded recursion would build G-code until the tab died.

One emitter for every travel leg: the first one out, each leg between strokes, the leg before a pen change, and the return home — which is as long as any and just as capable of hitting a magnet.

travelLegs is the same code the writer uses, exported rather than reimplemented: a preview that disagreed with the G-code would be reassuring about a detour that isn't happening. A test pins the two against each other. Only bent legs are drawn — rendering every pen-up move would bury the artwork in dashes.

An endpoint inside a zone means give up, not fail. There's no detour to a destination that's itself in the keep-out, and that case is geometry the operator has to move — already blocked before plotting.

The block message now names the magnet and the artwork: "the magnet at 245, 155 mm on Rect" rather than a count, so the operator knows which one to walk over and move.

Verification

mise run ci green — 372 tests, coverage 65.9 → 66.5% statements.

17 new tests: clear leg untouched, leg through / clipping a zone, several zones on one leg, overlapping zones, detour staying near the straight line (a detour is a nuisance, not a licence to cross the sheet), endpoint inside a zone, a leg straight through a centre (no outward direction exists there), a degenerate leg, routing between pen groups, the return home, travelLegs matching the emitted G-code point for point, the estimate costing the detour, and byte-for-byte identical output with no zones.

Driven in a browser: with a magnet on the straight line between two shapes, the emitted travel bends via a waypoint whose closest approach to the magnet is 12.47 mm against a 12 mm keep-out, and the detour is drawn on the canvas.

Not yet verified

Hardware — a job whose travel crosses a magnet has to be run to confirm the carriage actually clears it. Unchecked in the OpenSpec change.

🤖 Generated with Claude Code

Knowing where the magnets are was only half of it. `orderPolylines` picks the
next stroke by nearest distance, so on an A0 bed a pen-up leg can cross the
whole sheet in one straight line — over a magnet. The carriage strikes it at
travel feed, drags the sheet, and the work origin is gone.

The overlay could warn about geometry drawn *through* a magnet, because the
operator can fix that by moving something. It could say nothing useful about
travel, because the fix for travel is not to move anything — it is to go
around.

`routeAround` pushes a leg's closest approach out past the edge of the first
zone it clips, then routes each half the same way, to a bounded depth. The
zones are few and far apart, so this needs no general path planner; a
visibility graph would be the general answer to a problem this does not have.
The depth cap is there for the degenerate input, where unbounded recursion
would build G-code until the tab died.

Every travel leg goes through one emitter — the first one out, each leg
between strokes, the leg before a pen change, and the return home, which is as
long as any and just as capable of hitting a magnet.

`travelLegs` is the same code the writer uses, exported rather than
reimplemented: a preview that disagreed with the G-code would be reassuring
about a detour that is not happening. A test pins the two against each other.
Only bent legs are drawn — rendering every pen-up move would bury the artwork
in dashes.

The block message now names the magnet and the artwork it sits on. "2 magnets
are in the way" is not actionable; "the magnet at 245, 155 mm on Rect" sends
the operator to the right place on the bed.

With no magnets recorded the output is byte-for-byte what it was, pinned by a
test. The plot-time estimate costs the detours for free, since it walks the
emitted G-code.

Verified in the browser: with a magnet on the straight line between two
shapes, the emitted travel bends via a waypoint whose closest approach to the
magnet is 12.47 mm against a 12 mm keep-out, and the detour is drawn on the
canvas.

Closes #64

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@BernardJen
BernardJen merged commit e4ea13b into main Sep 12, 2026
2 checks passed
@BernardJen
BernardJen deleted the feat/magnet-avoid branch September 12, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Set magnet positions as keep-out zones the toolpath routes around

1 participant