From c5404507c7c724d1293ead32c640e68712615ab5 Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Fri, 31 Jul 2026 19:55:56 +0200 Subject: [PATCH] =?UTF-8?q?Design:=20an=20open=20sketch=20line=20can=20be?= =?UTF-8?q?=20clicked=20=E2=80=94=20region=20membership=20is=20not=20a=20l?= =?UTF-8?q?icence=20to=20be=20pointed=20at?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01LyRwbuq6fjn3VV9U9UvhBM --- src/slic3r/GUI/DesignSketchTool.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index 038cedc303..22bb1ced15 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -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 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 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) {