From bc4fb3b680a43ab35922af7b2f860a47915f977a Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Sat, 1 Aug 2026 17:38:44 +0200 Subject: [PATCH] Design: Text and SVG draw INTO the open sketch instead of beside it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of snaporca 1fb7786d9a (DesignPanel.cpp and DesignCanvas.cpp applied as patches; parity 30 / 16, shared files byte-identical). With a sketch open, Text/SVG outlines become ordinary Line entities via push_closed_lines() instead of a separate Sketch feature carrying rigid imported_regions — so the letters can be constrained, trimmed and extruded like anything drawn by hand. The buttons and offer actions arm Select first when in Sketch mode, since begin_sketch() does not run until a tool is armed. add_imported_regions() calls reset_autoedit(): without it the glyph contours entered the draw-then-edit queue and opened a Length field on the first segment, which freezes the canvas. Caught on the rig, not by reading. No sketch open: unchanged — a new Sketch feature, still dropped on a picked face. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LyRwbuq6fjn3VV9U9UvhBM --- src/slic3r/GUI/DesignCanvas.cpp | 8 ++++++++ src/slic3r/GUI/DesignCanvas.hpp | 2 ++ src/slic3r/GUI/DesignPanel.cpp | 32 +++++++++++++++++++++++++---- src/slic3r/GUI/DesignSketchTool.cpp | 23 +++++++++++++++++++++ src/slic3r/GUI/DesignSketchTool.hpp | 7 +++++++ 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/DesignCanvas.cpp b/src/slic3r/GUI/DesignCanvas.cpp index 3adc615aec..31f036f2bd 100644 --- a/src/slic3r/GUI/DesignCanvas.cpp +++ b/src/slic3r/GUI/DesignCanvas.cpp @@ -389,6 +389,14 @@ void DesignCanvas::set_sketch_construction(bool c) m_sketch_tool.set_construction(c); } +bool DesignCanvas::add_sketch_regions( + const std::vector>>& regions) +{ + const bool ok = m_sketch_tool.add_imported_regions(regions); + if (ok) request_repaint(); + return ok; +} + void DesignCanvas::set_sketch_polygon_sides(int n) { m_sketch_tool.set_polygon_sides(n); diff --git a/src/slic3r/GUI/DesignCanvas.hpp b/src/slic3r/GUI/DesignCanvas.hpp index bc6f1a6b8b..d09bbbc3f6 100644 --- a/src/slic3r/GUI/DesignCanvas.hpp +++ b/src/slic3r/GUI/DesignCanvas.hpp @@ -53,6 +53,8 @@ public: void set_sketch_tool(DesignSketchTool::Mode mode); void set_sketch_plane(const SketchPlane& plane); // re-plane the live sketch when a reference plane is clicked in 3D void set_sketch_construction(bool c); + // Text / SVG art into the LIVE sketch, as ordinary editable lines. False = no session. + bool add_sketch_regions(const std::vector>>& regions); void set_sketch_polygon_sides(int n); void set_sketch_polygon_circumscribed(bool c); void finish_sketch(); diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index f62b299da3..75a09492fe 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -1170,19 +1170,32 @@ DesignPanel::DesignPanel(wxWindow* parent) // the sketch tools, not in the generic Features strip. Each places the art // in-canvas, then commits via the Insert card's Confirm. { + // In Sketch MODE the art must land in the sketch — but begin_sketch() does not run + // until the first tool is armed, so pressing Sketch and then Text would find no + // session and commit a separate feature. Arm Select first: it starts the session on + // the picked plane without drawing anything, so Text behaves the same whether or not + // you had already drawn a line. (MODE vs SESSION — the distinction that has bitten + // this panel before.) + auto ensure_sketch = [this, select_tool] { + if (m_ui_mode == UiMode::Sketch && m_viewport && !m_viewport->is_sketching()) + select_tool(DesignSketchTool::Mode::Select, + _L("Select — click to select; Shift to add")); + }; auto* b_text = icon_btn("design_text", _L("Text — emboss text as a profile")); - b_text->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_add_text(); }); + b_text->Bind(wxEVT_BUTTON, [this, ensure_sketch](wxCommandEvent&) { + ensure_sketch(); on_add_text(); }); sadd(b_text); auto* b_svg = icon_btn("design_svg", _L("SVG — import an outline as a profile")); - b_svg->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { on_import_svg(); }); + b_svg->Bind(wxEVT_BUTTON, [this, ensure_sketch](wxCommandEvent&) { + ensure_sketch(); on_import_svg(); }); sadd(b_svg); // …and reachable from the offer's Create row. These were on the atlas's chrome_only // list under "document-level actions act on the DOCUMENT, not on a selection" — which // is not what they do: both call add_imported_sketch(), which drops the art ON a // picked solid face (SketchPlane::from_face, centred on it) exactly as Sketch does. // Selection-consuming profile creators, so they belong with the other Create verbs. - m_verb_actions["btn:text"] = [this] { on_add_text(); }; - m_verb_actions["btn:svg"] = [this] { on_import_svg(); }; + m_verb_actions["btn:text"] = [this, ensure_sketch] { ensure_sketch(); on_add_text(); }; + m_verb_actions["btn:svg"] = [this, ensure_sketch] { ensure_sketch(); on_import_svg(); }; } add_sep(m_tb_sketch); // In-canvas edit-op tools (drag gizmo / click label), grouped by family. @@ -4044,6 +4057,17 @@ void DesignPanel::add_imported_sketch( m_status->Refresh(); return; } + // DRAWING, not importing: if a sketch is open, the art belongs IN it. The outlines become + // ordinary line entities, so they can be constrained, trimmed and extruded with everything + // else on that plane. Committing a separate Sketch feature while the user is mid-sketch put + // the text on its own plane-origin feature and left the sketch they were drawing untouched. + if (m_viewport && m_viewport->is_sketching() && m_viewport->add_sketch_regions(regions)) { + m_status->SetForegroundColour(wxNullColour); + m_status->SetLabel(wxString::Format(_L("%s added to the sketch — Confirm to commit it"), + base_name)); + m_status->Refresh(); + return; + } m_doc.checkpoint(); // undo boundary: importing Text/SVG art m_feature_counter++; CadFeature f; diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index be217fc934..343f0f8fbb 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -2317,6 +2317,29 @@ void DesignSketchTool::push_line(const Vec2d& a, const Vec2d& b) m_entities.push_back(e); } +bool DesignSketchTool::add_imported_regions( + const std::vector>>& regions) +{ + if (!m_active) return false; // no session to draw into; caller makes a feature + const bool saved = m_construction; + m_construction = false; // art is real geometry, never construction lines + size_t before = m_entities.size(); + for (const auto& region : regions) + for (const auto& loop : region) + push_closed_lines(loop); // every glyph contour, holes included + m_construction = saved; + if (m_entities.size() == before) return false; // nothing importable — say so, do not lie + // Art is not "just drawn", so it must NOT enter the draw-then-edit queue. Without this the + // glyph contours are treated as fresh entities and a Length field opens on the first of + // them — on a word, that is one value editor per segment, and an open field freezes the + // canvas (snaporca-yce). reset_autoedit() marks every entity as already seen. + reset_autoedit(); + // The new lines carry no constraints, so the solver has nothing to move; resolve anyway so + // the degrees-of-freedom readout counts them instead of going stale. + resolve_live(); + return true; +} + void DesignSketchTool::push_closed_lines(const std::vector& corners) { const size_t n = corners.size(); diff --git a/src/slic3r/GUI/DesignSketchTool.hpp b/src/slic3r/GUI/DesignSketchTool.hpp index ed65970c8b..334169d75b 100644 --- a/src/slic3r/GUI/DesignSketchTool.hpp +++ b/src/slic3r/GUI/DesignSketchTool.hpp @@ -79,6 +79,13 @@ public: void begin_edit(const std::vector& entities, const std::vector& constraints, const SketchPlane& plane); + // Drop rigid 2D art (Text / SVG outlines) INTO the live sketch as ordinary line entities, + // so it joins the sketch being drawn instead of committing a separate Sketch feature. + // `regions` are loops in PLANE coordinates; every closed loop becomes a closed polyline, so + // the result is editable, constrainable and extrudable like anything else drawn by hand — + // unlike imported_regions, which are rigid and carry no solver entities. + // Returns false when no session is live, so the caller can fall back to a new feature. + bool add_imported_regions(const std::vector>>& regions); void set_tool(Mode mode); // switch tool, keep accumulated entities void set_plane(const SketchPlane& plane) { m_plane = plane; } // re-plane a live sketch (a reference plane was clicked mid-session); entities are 2D, re-lifted through the new plane void set_construction(bool c) { m_construction = c; }