Skip to content

feat(optimize): cut planner switches (best start point, prefer corners) - #541

Merged
knipknap merged 10 commits into
barebaric:mainfrom
switch87:feat/cut-planner
Oct 18, 2026
Merged

knipknap merged 10 commits into
barebaric:mainfrom
switch87:feat/cut-planner

Conversation

@switch87

@switch87 switch87 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Expose the cut planner in Rayforge's Optimize settings group: Choose Best
Start Point
and Prefer Corners (rayforge half of #523).

What's in this PR

  • Optimize transformer gains best_start_point and prefer_corners
    (both default off, so existing jobs are unchanged) and passes them through
    to raygeo.ops.transform.optimize.OptimizeSpec.
  • The Optimize settings group gains two switches:
    • Choose Best Start Point — enter closed paths at the vertex nearest the
      current position, hiding the seam of every loop;
    • Prefer Corners (only active while the above is on) — restrict the start
      vertices of a closed path to its sharp corners; paths without corners
      (circles) fall back to any vertex.
  • to_dict/from_dict roundtrip the new fields; the addon catalogs are
    updated for all languages.

Dependency

This PR needs the matching raygeo change
(barebaric/raygeo#54) to be released: the
OptimizeSpec(...) kwargs come from that version of raygeo. Until the
raygeo==1.61.x dependency is bumped, CI against the released wheel fails on
the new kwargs — the PR is meant to land together with (or right after) the
raygeo release.

Tests

rayforge/builtin_addons/rayforge-addon-post/tests/transformers/test_optimize_transformer.py
— new TestCutPlannerOptions covers serialization defaults and roundtrip,
to_spec pass-through, and the behavioural rotation of a closed path
(30 passed in the targeted file).

@switch87

Copy link
Copy Markdown
Contributor Author

The raygeo side has now shipped, so this PR is unblocked on that half.

raygeo v1.63.0 is on PyPI (tag v1.63.0 = the merge of raygeo#54), and it carries
best_start_point / prefer_corners on OptimizeSpec — the kwargs this PR passes through. It
also includes raygeo#55 (error-diffusion / halftone / newsprint dithering) and raygeo#56
(close_open_contours, join_open_contours, remove_duplicate_contours, matches).

What is left is the dependency bump itself: pixi.toml still pins raygeo = "==1.61.0", and CI
installs the released wheel under pixi run --frozen, so both the Lint and Backend Tests jobs
fail against 1.61.0:

  • Lint: No parameter named "best_start_point" / "prefer_corners" in
    post_processors/transformers/optimize_transformer.py, plus the matching attribute errors in
    test_optimize_transformer.py
  • Backend Tests: TypeError: OptimizeSpec.__new__() got an unexpected keyword argument 'best_start_point' across the optimize transformer tests

I have not touched pixi.toml/pixi.lock here on purpose: regenerating the lock needs pixi,
and mixing a dependency bump into this branch would put every other open PR into conflict. Happy
to do the bump as its own PR if that is wanted — otherwise this should land right after
raygeo==1.63.0 is pinned.

Two new options on the Optimize transformer and its settings group:
Choose Best Start Point (rotate closed paths to the vertex nearest the
head) and Prefer Corners (only start at sharp corners when the path has
any). Both default off, so existing jobs are unchanged. The backend
fields are provided by the raygeo cut-planner change.

Refs barebaric#523
@switch87

Copy link
Copy Markdown
Contributor Author

Correction to my comment above: the pin was already bumped — main carries raygeo==1.63.0 since 8ca6a0a, I was looking at a branch that had not been rebased. Rebased onto current main now (no conflicts, no dependency changes in this PR); the 30 targeted tests pass locally against the new pin, so CI should go green on this run.

The new strings stay wrapped in _(); the .po/.pot files are updated
once before each release.
Choosing the nearest vertex shortens travel; it does not hide the seam.
Hiding the seam is what Prefer Corners is for.

Refs barebaric#523
@switch87

Copy link
Copy Markdown
Contributor Author

Went over this one again against the architecture notes and pushed three small commits on top, no force-push:

  • 65640d3: the subtitle of Choose Best Start Point claimed it hides the seam, which it doesn't (it shortens travel); hiding the seam is what Prefer Corners does, so both subtitles now say that
  • fafcf5a: tests share one fixture and the inline comment is gone
  • 2b8066d: the two switches are documented on the Path Optimization page, in English and the seven translated pages (formatted with prettier)

The .po/.pot changes were already dropped in ef47cbc. The PR now only touches the Optimize transformer, its settings group, its tests and the docs.

The website figure generators were standalone scripts under
scripts/media/ with duplicated palette, font and output code. They now
live in a scripts/figures package with shared style and canvas modules
and a pixi figures task (python -m scripts.figures). Regenerating the
moved figures produces byte-identical images.
Add a scripts/figures/path_optimization generator that runs a sample
toolpath through the real raygeo optimizer and renders travel moves
before and after optimization, plus where the closed test path is
entered as drawn, at the nearest vertex (Choose Best Start Point) and
at the nearest corner (Prefer Corners).

Add the step-settings:contour:post:optimize screenshot target, which
opens the post-processing page with the Optimize group expanded and
both cut-planner switches on. Embed the figures and screenshot in the
path optimization docs for all languages.
The step-settings screenshot scripts duck-typed their way to the
post-processing page via hasattr(widget, "_group_dicts") and searched
expander rows by title, then swallowed failures behind a generic
logged message. The page now exposes get_transformer_group() and
get_expander_row(), which raise KeyError when the transformer is
absent, and the scripts preset the Optimize switches on the step
model, fail with a specific RuntimeError on every miss, and check the
screenshot result. Regenerating both targets reproduces the committed
images.
@knipknap

Copy link
Copy Markdown
Contributor

Extended this PR with documentation visuals and some cleanups (df9dc92, 433356f, 1de965a, 97c9747, plus a merge of main):

Merged main — the branch had drifted 75 commits behind; clean merge (only pixi.toml and the screenshot cli.py overlapped). The branch now pins raygeo==1.66.0, which already ships the OptimizeSpec kwargs, so the dependency caveat in the description is resolved.

Generated figures for path-optimization.md — new scripts/figures/path_optimization.py builds a sample toolpath, runs it through the real raygeo optimizer (Ops.apply_transformers), and renders:

  • path-optimization-before.webp / path-optimization-after.webp — travel moves before and after optimization
  • path-optimization-start-point.webp — the same closed path entered as drawn (1), at the vertex nearest the laser head with Choose Best Start Point (2), and at the nearest sharp corner with Prefer Corners (3); a dot marks the entry point, a small gap the seam

Because the figures run the actual optimizer, they cannot drift from real behavior.

Refactored the figure generators — the two one-off scripts under scripts/media/ moved into a scripts/figures package with shared style.py/canvas.py and a pixi run figures task. Regenerating the moved figures produces byte-identical images (md5-verified).

New screenshot target step-settings:contour:post:optimize (scripts/screenshot/step_settings_optimize.py) — the post-processing page with the Optimize group expanded and both new switches on. The collapsed post-processing screenshots are unchanged by this PR (the switches live inside the expanded group), so they were left untouched.

Docs — path-optimization.md and all seven translations embed the figures and the screenshot with translated alt text; pixi run site-format applied.

Public API instead of widget-tree hacks — the screenshot scripts reached the post-processing page via hasattr(widget, "_group_dicts") and searched expander rows by title, then swallowed failures behind a generic log line. PostProcessingPage now exposes get_transformer_group() / get_expander_row() (raising KeyError when the transformer is absent), both screenshot scripts use them, and every failure raises a specific error instead. Regenerating both affected targets reproduces the committed images.

Verification: lint clean; targeted optimize transformer tests 30 passed; test_post_processing.py UI test passed; full suite 7780 passed on push.

@knipknap
knipknap merged commit da24bf9 into barebaric:main Oct 18, 2026
30 of 32 checks passed

This branch was successfully deployed

1 active deployment
pypi — 97c9747f Deployed Oct 18, 2026 by knipknap via Build and Publish Wheel #2369
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.

2 participants