Design: an open sketch line can be clicked — region membership is not a licence to be pointed at

Reported from the rig: a committed sketch holding a single open line rendered on
the plate and could not be selected, so it could not be edited or deleted from
the viewport at all.

The viewport pick for committed sketches iterated region_loops(). That function
exists to find EXTRUDABLE regions and, as its own walk comment says, an open
chain "stalls" and is discarded. So for a sketch of open entities it returns an
empty list, the pick loop has nothing to iterate, and every click falls through
to bare plate. Not a tolerance problem and not a focus problem: there was no
candidate geometry to test against.

Whether a stroke bounds a closed region has nothing to do with whether the user
can point at it. The stroke test now covers every non-construction entity, and a
separate entity->region map preserves what a hit REPORTS, so a click inside a
closed loop still names that loop exactly as before. Region membership decides
the report, not whether the hit can happen.

The rest of the path was already written and simply unreachable: the panel's
handler has a region < 0 branch that selects the feature, highlights it in the
tree and says "Sketch selected — Extrude it, or Edit / Delete from the tree".
This makes existing behaviour reachable rather than adding new behaviour.

Verified on :11 with the pick tracer (SNAPORCA_PICK_TRACE=1), which is what
distinguished the two failure modes: before, the click reached the handler and
fell through to handle_solid_click ("no solid data"); after, it is consumed by
the display-sketch test and never reaches it, and the panel reads "Sketch
selected" with Sketch1 lit in the tree.

Construction geometry stays unpickable, matching region_loops' own filter. It is
the same class of bug — a construction line cannot be selected to delete it —
but including it risks construction stealing picks from real geometry, so it is
left as a separate decision.

Refs snaporca-e1p.

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 19:55:56 +02:00
co-authored by Claude Opus 5
parent 9f2b2bc511
commit c5404507c7
+19 -6
View File
@@ -7908,14 +7908,27 @@ bool DesignSketchTool::on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas)
const Vec2d p8 = d.plane.project(ray8.a, ray8.vector());
const double tol = std::max(1e-3, (p8 - p).norm());
const std::vector<RegionLoop> loops = region_loops(d.entities);
for (int r = 0; r < int(loops.size()); ++r) {
for (int ei : loops[r].ents) {
if (ei < 0 || ei >= int(d.entities.size())) continue;
const double ed = entity_pick_dist(p, d.entities[ei]);
if (ed <= tol * 3.0 && ed < edge_d) { edge_d = ed; edge_feat = d.feature; edge_reg = r; }
// region_loops exists to find EXTRUDABLE regions, and by design it discards open
// chains — its own walk comment says so. Using it as the pick index meant a
// committed sketch of open lines had no pickable geometry whatsoever: you could see
// the strokes and could not click one of them, so there was no way to select it, and
// therefore no way to edit or delete it either. Whether a stroke bounds a region has
// nothing to do with whether the user can point at it. Region membership decides
// what a hit REPORTS, not whether the hit can happen.
std::vector<int> ent_region(d.entities.size(), -1);
for (int r = 0; r < int(loops.size()); ++r)
for (int ei : loops[r].ents)
if (ei >= 0 && ei < int(ent_region.size())) ent_region[ei] = r;
for (int ei = 0; ei < int(d.entities.size()); ++ei) {
if (d.entities[ei].construction) continue; // as region_loops filters it
const double ed = entity_pick_dist(p, d.entities[ei]);
if (ed <= tol * 3.0 && ed < edge_d) {
edge_d = ed; edge_feat = d.feature; edge_reg = ent_region[ei];
}
if (face_feat < 0 && point_in_poly(p, loops[r].poly)) { face_feat = d.feature; face_reg = r; }
}
for (int r = 0; r < int(loops.size()); ++r)
if (face_feat < 0 && point_in_poly(p, loops[r].poly)) { face_feat = d.feature; face_reg = r; }
}
// A precise hit on a loop outline wins over the solid face beneath it.
if (edge_feat >= 0) {