Design: escalate on the entity that was picked, and let the chip follow the window

Both found by Kimi reviewing the previous two commits, both then reproduced here
before being touched.

snaporca-97z. The re-pick escalation required m_solid_sel, m_sel_body, m_sel_face AND
m_sel_edge to all match the previous pick. That looked stricter and was wrong: the
edge branch sets only m_sel_edge and m_solid_sel, leaving m_sel_face as whichever face
the ray happened to enter through — and a shared edge is entered through a different
face depending on which side you view it from. So picking an edge and picking that
same edge again from the other side compared equal edges, unequal faces, and refused
the escalation the status line had just promised. Now the comparison is made at the
level that was picked and nothing else. The edge id is already the stable global one
from edge_index_of, so it identifies the edge without help from the face.

Reproduced on the rig without needing to orbit, since two clicks 8px apart across an
edge enter through different faces:

  pick -> sel=3 body=0 face=5 edge=3
  ray  -> body=0 face=0
  re-pick -> escalated to whole body 0
  pick -> sel=1 body=0 face=-1 edge=-1

Same edge, face 5 then face 0, escalation fires. The old condition could not.

The frame-move case. The chip is anchored at an absolute screen position, and until now
nothing told it the window had moved — only a resize, a status change or a tab switch
re-placed it. Dragging the window by its title bar left it stranded where it was,
verified on the rig by moving the frame and watching it stay put. wxEVT_MOVE on the
top-level frame, alongside the ICONIZE and ACTIVATE binds from the previous commit.

Two related cases are filed rather than bound, because the list of window-geometry
events to chase is exactly what snaporca-lcq argues should stop: a layout change that
translates the canvas without resizing it, and wxEVT_DPI_CHANGED.

Not fixed, deliberately, and recorded on snaporca-97z: clicking the same FACE but
landing within the vertex or edge tolerance resolves to a different kind and so does
not escalate — that is the "smallest thing under the cursor" rule working as
documented; and a vertex re-pick after moving the body compares stale world
coordinates.

Verified on both rigs. The cross-face edge case was exercised on orca_cad;
DesignSketchTool.cpp is byte-identical across the forks, so snaporca inherits it, and
its face-level escalation and empty-click clear were re-checked there directly.

Fork parity unchanged: DesignSketchTool.cpp 0, DesignCanvas.cpp 16.
This commit is contained in:
Tommaso Bianchi
2026-08-03 14:17:57 +02:00
parent 6d1a4078ca
commit 93395b7888
2 changed files with 21 additions and 3 deletions
+6
View File
@@ -176,6 +176,12 @@ DesignCanvas::DesignCanvas(wxWindow* parent)
top->Bind(wxEVT_ACTIVATE, [this](wxActivateEvent& e) {
show_status_hud(e.GetActive()); e.Skip();
});
// The anchor is an ABSOLUTE SCREEN position (ClientToScreen below), so moving the window
// moves the canvas out from under a chip that stays where it was. Dragging the frame by
// its title bar left the chip stranded mid-viewport until the next size, status or tab
// change happened to re-place it. Nothing on the canvas fires for a move that does not
// also resize, so it has to come from the frame.
top->Bind(wxEVT_MOVE, [this](wxMoveEvent& e) { place_status_hud(); e.Skip(); });
}
refresh_bed();
+15 -3
View File
@@ -3073,9 +3073,21 @@ bool DesignSketchTool::handle_solid_click(GLCanvas3D& canvas, const wxMouseEvent
// Double-click is safe: wx sends Down/Up/DClick/Up, and only the first Up carries a
// pending press, so a fast double-click zooms to fit and picks ONCE. Escalation needs two
// separate clicks, the same "click, pause, click" distinction a file manager uses.
if (m_solid_sel == prev_kind && m_sel_body == prev_body && m_sel_face == prev_face
&& m_sel_edge == prev_edge
&& (m_solid_sel != SolidSel::Vertex || (m_sel_vertex_pt - prev_vtx).norm() < 1e-9)) {
//
// "The same thing" is compared AT THE LEVEL THAT WAS PICKED, and nothing else. Requiring
// every field to match looked stricter and was simply wrong: an edge pick leaves m_sel_face
// set to whichever face the ray happened to hit, and a shared edge is reached through a
// different face depending on which side of the body you are looking from. So picking an
// edge, orbiting, and clicking that same edge from the other side left m_sel_edge equal and
// m_sel_face different, and the escalation the status line had just promised did not happen.
// The edge id here is already the STABLE GLOBAL one (edge_index_of, a few lines up) — it
// identifies the edge on its own and does not need the face to disambiguate it.
const bool same_pick = m_solid_sel == prev_kind && m_sel_body == prev_body
&& (m_solid_sel == SolidSel::Vertex ? (m_sel_vertex_pt - prev_vtx).norm() < 1e-9
: m_solid_sel == SolidSel::Edge ? m_sel_edge == prev_edge
: m_solid_sel == SolidSel::Face ? m_sel_face == prev_face
: true);
if (same_pick) {
select_body(m_sel_body); // clears face/edge/vertex, tints the whole body
dp_pick_trace("re-pick -> escalated to whole body %d", m_sel_body);
}