feat(plot): route pen-up travel around the magnets - #75
Merged
Merged
Conversation
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>
10 tasks
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.
Closes #64.
Why
Knowing where the magnets are was only half of it.
orderPolylinespicks 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
routeAroundpushes 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.
travelLegsis 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 cigreen — 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,
travelLegsmatching 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