Design: left-drag sweeps a rubber band, and it takes the whole body

The pick cycle died two commits ago and left no viewport route to a whole
body at all: one click resolves vertex, edge or face, double-click is
already zoom-to-fit, and the only way to take a body was the Bodies list —
a geometry-first violation for as long as it stood. The rubber band is that
route.

Left-drag is the gesture, as asked. That button was orbit, so this canvas
now maps the mouse the way every CAD the user already knows does: left
selects, middle orbits, right pans. The change is a single flag on
GLCanvas3D set only by DesignCanvas, so Prepare and Preview keep the mouse
their users learned. Sketch mode inherits the same mapping, which is the
consistent reading — Design is one modality, not two.

Past an 8 px budget a press becomes a sweep, anchored at the ORIGINAL press
point rather than at the frame where the threshold was crossed, so the
first few pixels are not lost. Below the budget it is still a click and the
existing vertex/edge/face pick runs untouched. Sampling is the display
mesh's triangle vertices plus centroids — the same points the ray pick
tests, already in world coordinates — and the body with the most samples
inside wins, because the selection callback downstream carries one body.
Crossing semantics: touching selects. Enclosed-only for left-to-right and
crossing for right-to-left is the fuller CAD convention and is deferred,
not forgotten; with one selectable body it would have bought nothing.

Two defects fixed on the way, both found by exercising this:

Right-drag pans, and every pan ended by popping the offer over wherever the
camera stopped — the context menu arriving as the reward for moving the
view. The offer is now the release of a STATIONARY right-click, at the same
8 px budget the pick uses.

The selection handler wrote m_status twice. Only the later write ever
reached the screen, so the earlier block had been dead since it was
written, and its labels drifted out of step with the live ones unnoticed —
including a vertex fix I made this morning in the branch that never
renders. Deleted, with a note saying why, rather than left as two writers
for the next person to pick the wrong one.

Verified on :11 against a fresh build: click takes face 5; left-drag across
the body reports "selected (whole body)" with the whole solid tinted and
the camera unmoved; left-drag over empty space clears; stationary
right-click opens the offer; right-drag pans with no menu; middle-drag
orbits. Precedence re-checked after the deletion — face at 25 px from the
corner, vertex from 10 px in.

Refs snaporca-9xw.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LyRwbuq6fjn3VV9U9UvhBM
This commit is contained in:
Tommaso Bianchi
2026-07-31 18:11:59 +02:00
co-authored by Claude Opus 5
parent 33f97d259b
commit bb403b82cf
7 changed files with 124 additions and 18 deletions
+4 -13
View File
@@ -3133,19 +3133,10 @@ DesignPanel::DesignPanel(wxWindow* parent)
// Say what got picked. Without this the ONLY feedback is the viewport highlight, so a
// pick that registers but draws faintly is indistinguishable from one that never
// happened — which is precisely how this failure was reported and why it resisted
// diagnosis. Cards that show their own labels still do.
if (m_status != nullptr) {
m_status->SetForegroundColour(wxNullColour);
if (level <= 0)
m_status->SetLabel(_L("Selection cleared"));
else if (level == 1)
m_status->SetLabel(wxString::Format(_L("Body %d selected"), body + 1));
else if (level == 2)
m_status->SetLabel(wxString::Format(_L("Body %d, face %d"), body + 1, face));
else
m_status->SetLabel(wxString::Format(_L("Body %d, edge %d selected"), body + 1, edge));
m_status->Refresh();
}
// diagnosis. Cards that show their own labels still do. The label itself is written
// ONCE, at the end of this handler — a second writer here only ever produced text that
// the later one overwrote before a frame was drawn, and reading it as the live string
// is how a vertex pick came to be "fixed" in a branch that never reaches the screen.
// If the Fillet/Chamfer card is open, re-anchor (or drop) the radius arrow on the new pick
// and rebuild the ghost — once an edge is picked the preview-only mode hides the base body.
if (m_active == Tool::Dressup) { sync_dressup_target(); update_fillet_gizmo(); refresh_preview(); }