The sketch says what to do next, and construction geometry looks like it

User report, 2026-08-23, after using the freshly deployed build: "selection and
removal of existing elements of the 2d sketch is not intuitive, and the bottom ui
text does not illustrate what the user has to do to properly use the selected
tools. Mirror, for example, does not indicate: first select mirror line then
entities to be selected, and there is no UI indication of what is being selected.
normally, costruction lines are dotted." Four defects, all of them in the 2D
vocabulary this fork's charter puts at the centre, and all four fixed here.

snaporca-1c0c (P1) — the prompt was written ONCE, when the tool was armed.
DesignPanel's select_tool lambda set a sentence and nothing ever revised it, so
every step after the first was unguided: Mirror said "pick axis, then entities"
and then never said which of the two you were on; Escape silently downgraded an
armed tool to Select (request_exit is layered: anchors, then tool, then session)
while the line still named the tool you had left; and nothing ever mentioned that
Del removes a selection. The fix moves the line off the arm event and onto the
tool's LIVE state. DesignSketchTool::emit_step_hint() reports (mode, step, picks)
whenever that triple moves, from render() — the one place every state change in
this tool passes through. Putting it there instead of in the thirty-odd branches
of on_mouse is the whole point: a per-call-site notification is a thing the next
tool forgets to add, and it costs three int comparisons a frame. DesignPanel owns
the words, in ONE table (sketch_step_prompt), whose step numbers are the same ones
render() previews and on_mouse consumes, so the description cannot drift from the
code that reads the clicks. Every tool now names the gesture that ENDS it, because
none of them was discoverable: an empty click applies an edit-op or a transform,
right-click cancels it, Esc goes back to Select.

snaporca-vd6v (P2) — Mirror mirrors its axis pick and its target picks into
m_selection, so both painted white and the picture could not answer "what did I
select as what". The edit-op's first pick — Mirror's axis, Fillet/Chamfer's first
line — now paints violet. Violet and not cyan: cyan means SELECTED in this canvas
and nothing else may wear it, a rule this file already carries in writing.

snaporca-imlq (P2) — construction geometry drew as a solid grey line. Every CAD
dashes it, and grey alone does not read as "reference" against the
under-constrained orange. dash_polyline() chops the polyline before it reaches
draw_quad_strip, with the dash and gap in world units scaled by units-per-pixel,
so a dash keeps its size on screen instead of becoming a solid line when you zoom
out and three dashes when you zoom in.

snaporca-oql1 (P2) — Backspace now deletes as Del does. On every laptop this runs
on, Del is a chord and Backspace is what a hand reaches for. The Select-mode
prompt states the rest (Shift-click adds, double-click takes the loop, Del
removes), and the first step of every armed tool names the Esc route back to
Select, which was the invisible half of "selection is not intuitive".

Also, on the same report: the sketch stroke half-width goes 0.6 -> 0.3 mm. At 1.2
mm wide the orange line swallowed a short segment and hid which of two near
parallel lines the cursor was on. One constant, because all twenty call sites of
draw_quad_strip are sketch strokes.

Retired on the way: the on_sketch_selection_changed status writer. It said "N
selected — Delete removes them" while an edit-op mirrored its picks into the
selection, i.e. in the middle of a Mirror gesture, where Delete does nothing of
the sort. on_sketch_step says the true thing for Select and says nothing false
anywhere else. And the live length/angle readout is now APPENDED to the step
guidance rather than replacing it: it fires on every mouse move, so it used to
erase the instruction for the step in progress one move after the click that
started it.

Two false trails, recorded so the next session does not walk them again:
  - DesignPanel.hpp deliberately does not include DesignSketchTool.hpp, so the
    panel's handler takes the mode as an int and the .cpp casts it back. The
    first attempt put Mode in the header signature and the build said only
    "expected ',' or '...' before 'mode'".
  - The offer ladder failed six properties against a perfectly good binary
    because I had relaunched the rig myself without SNAPORCA_KEYTRACE=1, and its
    [OFFER] trace lines ARE its instrument. A ladder with no instrument reports
    "None", which reads exactly like a regression in the offer. ladder-all.sh
    launches it correctly; a hand relaunch must too.

VERIFIED, not merely compiled. Driven on the headless rig with synthetic mouse
and keyboard, and photographed at each step: "Mirror — first click the LINE to
mirror about (a construction line works)  ·  Esc goes back to Select" ->
"Mirror — axis set  ·  now click the entities to mirror  ·  right-click cancels"
-> "Mirror — axis set  ·  1 to mirror  ·  click another to add or remove it  ·
click empty space to apply", with the axis violet, its target white, and the
construction lines dashed while real geometry stays solid.

Full gate green afterwards (scripts/ladder-all.sh, ALL LADDERS HELD):
  offer table vs the atlas          OK
  kernel suite                      188 test cases, 2532 assertions
  engine ladder                     rungs 1-8, ALL RUNGS HELD
  corpus rung                       977-sheet drawing corpus, every 20th -> 49
                                    sampled, 39 gradeable, 39 clean
  corpus scale rung                 the 6 heaviest sheets, all clean
  gesture ladder                    93/93 properties, real mouse and keyboard
  offer ladder                      108/108 properties, through the right-click
                                    menu and the verbs behind it

That harness is the reason a UX change of this size can be made in one pass and
believed: 93 + 108 properties are driven the way a person drives the app, and the
977-sheet corpus keeps the engine underneath them honest against real drawings
rather than against my own arithmetic.
This commit is contained in:
Tommaso Bianchi
2026-08-23 13:46:21 +02:00
parent 3c7105202a
commit 65e2b6f626
6 changed files with 306 additions and 13 deletions
+5
View File
@@ -573,6 +573,11 @@ void DesignCanvas::set_on_solve_state(std::function<void(int, bool, bool)> cb)
m_sketch_tool.on_solve_state = std::move(cb);
}
void DesignCanvas::set_on_sketch_step(std::function<void(DesignSketchTool::Mode, int, int)> cb)
{
m_sketch_tool.on_step_changed = std::move(cb);
}
void DesignCanvas::apply_segment_length(double len)
{
m_sketch_tool.apply_segment_length(len);