Skip to content

Design a panel's surface in the app, instead of by hand in the XML - #7

Merged
stoatworks-labs merged 1 commit into
mainfrom
worktree-surface-designer
Aug 31, 2026
Merged

Design a panel's surface in the app, instead of by hand in the XML#7
stoatworks-labs merged 1 commit into
mainfrom
worktree-surface-designer

Conversation

@stoatworks-labs

Copy link
Copy Markdown
Owner

Surfaces have existed since panels stopped being flat, but the only way to shape one was to type control points into the stage file. The inspector now picks the kind — Flat, Arc or Lattice — and the Previz view drags a lattice's control points about.

What is in it

  • Inspector: a surface kind picker; arc sweep with radius/chord/depth reported beside it; lattice columns/rows, a per-point X/Y/Z editor, Reset and Flatten.
  • Previz: the selected panel's surface drawn as a wireframe, its control points as handles you can click and drag. Dragging anywhere else still orbits.
  • Core: Surface::bake_lattice / arc_metrics / set_point, Panel::local_of / stage_of, Camera::project / ray / forward, and geom::Ray.

Three decisions worth keeping

  • Conversions sample the old shape rather than rebuilding it from parameters, so switching an arc to a lattice mid-edit does not move the picture, and resizing the grid keeps the shape. Re-baking a lattice at its own size is the identity — the spinners re-bake on every change.
  • Handles are picked from press_origin, not from where the pointer is when egui decides a press has become a drag. By that frame the pointer has left the handle, and hit-testing the live position picks nothing at all.
  • A handle drags in the plane through it facing the camera, so depth is the one thing a pull cannot change.

Two things found on the way

  • The viewport was rendered at the window's size and squashed into the rect that shows it — previz at the wrong aspect, and an emulation zoom that did not mean what it said. Had to go before handles could be dragged.
  • The widgets are clickable after all, headlessly: egui::Context::run_ui takes a RawInput, so pointer events drive the real widget code with no window, no GPU and no NDI. The whole pick-drag-orbit path is tested that way, and it caught the press_origin bug before the app was ever run.

153 tests pass. Verified end to end as well: a real Arena 7.27 import, given an arc and a pulled lattice, renders through unmapper render --previz.

The formatting churn in the touched files is rustfmt catching up on lines that were already drifting.

🤖 Generated with Claude Code

Surfaces have existed since the day panels stopped being flat, but the only
way to shape one was to type control points into the stage file. The
inspector now picks the kind — Flat, Arc or Lattice — and the Previz view
drags a lattice's points about.

Three decisions worth keeping:

Conversions sample the old shape (Surface::bake_lattice) rather than
rebuilding it from parameters, so switching an arc to a lattice mid-edit does
not move the picture, and resizing the grid keeps the shape it had. Re-baking
a lattice at its own size is the identity, which matters because the columns
and rows spinners re-bake on every change.

Handles are picked from press_origin, not from where the pointer is when egui
decides a press has become a drag — by that frame the pointer has already left
the handle, and hit-testing the live position picks nothing at all.

A handle drags in the plane through it facing the camera, so depth is the one
thing a pull cannot change; any other plane lets a point run away at a
glancing view and pushes the wall through the set.

Also fixes the viewport being rendered at the *window's* size and squashed
into the rect that shows it: previz ran at the wrong aspect and the emulation
view's zoom did not mean what it said. It had to go before handles could be
dragged, because an overlay computed from the true camera lands nowhere near a
stretched image.

And the widgets turn out to be clickable after all: egui::Context::run_ui
takes a RawInput, so pointer events drive the real widget code with no window,
no GPU and no NDI. The whole pick-drag-orbit path is tested that way, and it
caught the press_origin bug before the app was ever run.

153 tests. The formatting churn in the files touched is rustfmt catching up on
lines that were already drifting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@stoatworks-labs
stoatworks-labs merged commit 3792b00 into main Aug 31, 2026
1 check passed
@stoatworks-labs
stoatworks-labs deleted the worktree-surface-designer branch August 31, 2026 05:05
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.

1 participant