From 6e11cfc49b88046678648ca91a3adad7e981e4ee Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Tue, 30 Jun 2026 22:01:54 +0200 Subject: [PATCH] Design: Measure-style dimension labels + restore English-only pin + UX consistency Dimensions (uniform with Prepare/Preview): - Repurpose DesignSketchTool::draw_text -> new draw_dim_label that renders each sketch dimension as the exact Prepare "Measure" gizmo label: white ImGui text in a translucent-white box, positioned via the existing world_to_screen_px projection inside the active ImGui frame. All 29 label call sites convert with no churn; the bespoke Hershey vector font is retired. - dim_text appends mm/in on linear dims (angles keep the degree sign). - Placed-dimension leaders simplified to a single point-to-point line + arrows in a neutral colour (no extension lines), matching the Measure look. i18n (Design tab pinned English, per the UX contract): - Restore the lost "#undef _L / #define _L(s) wxString::FromUTF8(s)" override atop DesignPanel.cpp so one lever de-translates the whole tab, ending the half-EN/IT state. Wrap all ~54 dropdown options in _L so the single lever governs them. - feature_type_name left untranslated (it feeds the MCP JSON, machine-facing). UX consistency: - Remove the per-card Confirm/Cancel buttons from the Value card; the single ribbon action bar now owns value confirm/cancel via an m_value_cont guard in tool_confirm/tool_cancel (one confirm surface, per contract). - Unify the eight divergent "needs a body" status messages to one template. Both forks; DesignSketchTool.{cpp,hpp} byte-identical across forks. Built clean. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q --- src/slic3r/GUI/DesignPanel.cpp | 122 ++++++++++++++-------------- src/slic3r/GUI/DesignSketchTool.cpp | 104 ++++++++++++------------ src/slic3r/GUI/DesignSketchTool.hpp | 2 + 3 files changed, 114 insertions(+), 114 deletions(-) diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index 6a242b8d97..5180169163 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -41,6 +41,15 @@ #include "slic3r/GUI/MainFrame.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +// English-only pin for the Design tab (see snaporca-design-ux-contract): one lever +// de-translates this whole TU so our strings never half-translate against the host's +// localized chrome. Host UI still follows the app locale; only this tab is pinned EN. +// GOTCHA: every _L(...) in this file must take a STRING LITERAL (FromUTF8 wants const char*). +#ifdef _L +#undef _L +#endif +#define _L(s) wxString::FromUTF8(s) + namespace Slic3r { namespace GUI { // Format a value with the international ('.') decimal separator regardless of the @@ -815,16 +824,16 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* form = new wxFlexGridSizer(2, 6, 8); m_shape = new wxChoice(m_form, wxID_ANY); - m_shape->Append("Rectangle"); - m_shape->Append("Circle"); + m_shape->Append(_L("Rectangle")); + m_shape->Append(_L("Circle")); m_shape->SetSelection(0); form->Add(new wxStaticText(m_form, wxID_ANY, _L("Shape")), 0, wxALIGN_CENTER_VERTICAL); form->Add(m_shape); m_plane = new wxChoice(m_form, wxID_ANY); - m_plane->Append("XY"); - m_plane->Append("XZ"); - m_plane->Append("YZ"); + m_plane->Append(_L("XY")); + m_plane->Append(_L("XZ")); + m_plane->Append(_L("YZ")); m_plane->SetSelection(0); form->Add(new wxStaticText(m_form, wxID_ANY, _L("Plane")), 0, wxALIGN_CENTER_VERTICAL); form->Add(m_plane); @@ -901,17 +910,17 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* dform = new wxFlexGridSizer(2, 6, 8); m_dressup_type = new wxChoice(m_form, wxID_ANY); - m_dressup_type->Append("Fillet"); - m_dressup_type->Append("Chamfer"); + m_dressup_type->Append(_L("Fillet")); + m_dressup_type->Append(_L("Chamfer")); m_dressup_type->SetSelection(0); dform->Add(new wxStaticText(m_form, wxID_ANY, _L("Dress-up")), 0, wxALIGN_CENTER_VERTICAL); dform->Add(m_dressup_type); m_face_group = new wxChoice(m_form, wxID_ANY); - m_face_group->Append("Top"); // index 0 -> FaceGroup::Top - m_face_group->Append("Bottom"); // 1 -> Bottom - m_face_group->Append("Lateral"); // 2 -> Lateral - m_face_group->Append("All"); // 3 -> All + m_face_group->Append(_L("Top")); // index 0 -> FaceGroup::Top + m_face_group->Append(_L("Bottom")); // 1 -> Bottom + m_face_group->Append(_L("Lateral")); // 2 -> Lateral + m_face_group->Append(_L("All")); // 3 -> All m_face_group->SetSelection(3); dform->Add(new wxStaticText(m_form, wxID_ANY, _L("Edges")), 0, wxALIGN_CENTER_VERTICAL); dform->Add(m_face_group); @@ -930,9 +939,9 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* hform = new wxFlexGridSizer(2, 6, 8); m_hole_plane = new wxChoice(m_form, wxID_ANY); - m_hole_plane->Append("XY"); - m_hole_plane->Append("XZ"); - m_hole_plane->Append("YZ"); + m_hole_plane->Append(_L("XY")); + m_hole_plane->Append(_L("XZ")); + m_hole_plane->Append(_L("YZ")); m_hole_plane->SetSelection(0); // Picking a plane here is an explicit choice: drop any on-face hijack (a stale face pick // could keep m_hole_on_face true, so the dropdown was ignored and the hole drilled on the @@ -978,9 +987,9 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* tform = new wxFlexGridSizer(2, 6, 8); m_thread_plane = new wxChoice(m_form, wxID_ANY); - m_thread_plane->Append("XY"); - m_thread_plane->Append("XZ"); - m_thread_plane->Append("YZ"); + m_thread_plane->Append(_L("XY")); + m_thread_plane->Append(_L("XZ")); + m_thread_plane->Append(_L("YZ")); m_thread_plane->SetSelection(0); tform->Add(new wxStaticText(m_form, wxID_ANY, _L("Thread plane")), 0, wxALIGN_CENTER_VERTICAL); tform->Add(m_thread_plane); @@ -1053,17 +1062,17 @@ DesignPanel::DesignPanel(wxWindow* parent) rform->Add(m_revolve_angle); m_revolve_axis = new wxChoice(m_form, wxID_ANY); - m_revolve_axis->Append("Plane X"); - m_revolve_axis->Append("Plane Y"); + m_revolve_axis->Append(_L("Plane X")); + m_revolve_axis->Append(_L("Plane Y")); m_revolve_axis->SetSelection(0); rform->Add(new wxStaticText(m_form, wxID_ANY, _L("Axis")), 0, wxALIGN_CENTER_VERTICAL); rform->Add(m_revolve_axis); m_revolve_mode = new wxChoice(m_form, wxID_ANY); - m_revolve_mode->Append("New"); - m_revolve_mode->Append("Add"); - m_revolve_mode->Append("Cut"); - m_revolve_mode->Append("Intersect"); + m_revolve_mode->Append(_L("New")); + m_revolve_mode->Append(_L("Add")); + m_revolve_mode->Append(_L("Cut")); + m_revolve_mode->Append(_L("Intersect")); m_revolve_mode->SetSelection(0); rform->Add(new wxStaticText(m_form, wxID_ANY, _L("Mode")), 0, wxALIGN_CENTER_VERTICAL); rform->Add(m_revolve_mode); @@ -1090,10 +1099,10 @@ DesignPanel::DesignPanel(wxWindow* parent) sform->Add(m_sweep_path); m_sweep_mode = new wxChoice(m_form, wxID_ANY); - m_sweep_mode->Append("New"); - m_sweep_mode->Append("Add"); - m_sweep_mode->Append("Cut"); - m_sweep_mode->Append("Intersect"); + m_sweep_mode->Append(_L("New")); + m_sweep_mode->Append(_L("Add")); + m_sweep_mode->Append(_L("Cut")); + m_sweep_mode->Append(_L("Intersect")); m_sweep_mode->SetSelection(0); sform->Add(new wxStaticText(m_form, wxID_ANY, _L("Mode")), 0, wxALIGN_CENTER_VERTICAL); sform->Add(m_sweep_mode); @@ -1110,8 +1119,8 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* pform = new wxFlexGridSizer(2, 6, 8); m_pattern_type = new wxChoice(m_form, wxID_ANY); - m_pattern_type->Append("Linear"); - m_pattern_type->Append("Circular"); + m_pattern_type->Append(_L("Linear")); + m_pattern_type->Append(_L("Circular")); m_pattern_type->SetSelection(0); pform->Add(new wxStaticText(m_form, wxID_ANY, _L("Type")), 0, wxALIGN_CENTER_VERTICAL); pform->Add(m_pattern_type); @@ -1125,8 +1134,8 @@ DesignPanel::DesignPanel(wxWindow* parent) pform->Add(m_pattern_spacing); m_pattern_dir = new wxChoice(m_form, wxID_ANY); - m_pattern_dir->Append("Plane X"); - m_pattern_dir->Append("Plane Y"); + m_pattern_dir->Append(_L("Plane X")); + m_pattern_dir->Append(_L("Plane Y")); m_pattern_dir->SetSelection(0); pform->Add(new wxStaticText(m_form, wxID_ANY, _L("Direction")), 0, wxALIGN_CENTER_VERTICAL); pform->Add(m_pattern_dir); @@ -1147,9 +1156,9 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* bform = new wxFlexGridSizer(2, 6, 8); m_bool_op = new wxChoice(m_form, wxID_ANY); - m_bool_op->Append("Union (join)"); - m_bool_op->Append("Subtract (cut)"); - m_bool_op->Append("Intersect"); + m_bool_op->Append(_L("Union (join)")); + m_bool_op->Append(_L("Subtract (cut)")); + m_bool_op->Append(_L("Intersect")); m_bool_op->SetSelection(0); m_bool_op->Bind(wxEVT_CHOICE, [this](wxCommandEvent&) { refresh_preview(); }); bform->Add(new wxStaticText(m_form, wxID_ANY, _L("Operation")), 0, wxALIGN_CENTER_VERTICAL); @@ -1254,8 +1263,8 @@ DesignPanel::DesignPanel(wxWindow* parent) plform->Add(m_plane_tilt); m_plane_tilt_axis = new wxChoice(m_form, wxID_ANY); - m_plane_tilt_axis->Append("Base X"); - m_plane_tilt_axis->Append("Base Y"); + m_plane_tilt_axis->Append(_L("Base X")); + m_plane_tilt_axis->Append(_L("Base Y")); m_plane_tilt_axis->SetSelection(0); plform->Add(new wxStaticText(m_form, wxID_ANY, _L("Tilt axis")), 0, wxALIGN_CENTER_VERTICAL); plform->Add(m_plane_tilt_axis); @@ -1297,10 +1306,10 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* lform = new wxFlexGridSizer(2, 6, 8); m_loft_mode = new wxChoice(m_form, wxID_ANY); - m_loft_mode->Append("New"); - m_loft_mode->Append("Add"); - m_loft_mode->Append("Cut"); - m_loft_mode->Append("Intersect"); + m_loft_mode->Append(_L("New")); + m_loft_mode->Append(_L("Add")); + m_loft_mode->Append(_L("Cut")); + m_loft_mode->Append(_L("Intersect")); m_loft_mode->SetSelection(0); lform->Add(new wxStaticText(m_form, wxID_ANY, _L("Mode")), 0, wxALIGN_CENTER_VERTICAL); lform->Add(m_loft_mode); @@ -1365,15 +1374,6 @@ DesignPanel::DesignPanel(wxWindow* parent) vrow->Add(new wxStaticText(m_form, wxID_ANY, _L("Value")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8); vrow->Add(m_value_input, 0, wxALIGN_CENTER_VERTICAL); m_box_value->Add(vrow, 0, wxLEFT | wxRIGHT | wxTOP, 12); - - auto* row = new wxBoxSizer(wxHORIZONTAL); - auto* ok = new wxButton(m_form, wxID_ANY, _L("✓ Confirm")); - ok->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { confirm_value(); }); - auto* no = new wxButton(m_form, wxID_ANY, _L("✗ Cancel")); - no->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { cancel_value(); }); - row->Add(ok, 0, wxRIGHT, 8); - row->Add(no, 0); - m_box_value->Add(row, 0, wxALL, 12); } root->Add(m_box_value, 0, wxEXPAND); @@ -1387,7 +1387,7 @@ DesignPanel::DesignPanel(wxWindow* parent) auto* prow = new wxBoxSizer(wxHORIZONTAL); prow->Add(new wxStaticText(m_form, wxID_ANY, _L("Plane")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 8); m_draw_plane = new wxChoice(m_form, wxID_ANY); - m_draw_plane->Append("XY"); m_draw_plane->Append("XZ"); m_draw_plane->Append("YZ"); + m_draw_plane->Append(_L("XY")); m_draw_plane->Append(_L("XZ")); m_draw_plane->Append(_L("YZ")); m_draw_plane->SetSelection(0); prow->Add(m_draw_plane, 0, wxALIGN_CENTER_VERTICAL); m_box_sketch_session->Add(prow, 0, wxLEFT | wxRIGHT | wxTOP, 12); @@ -2526,7 +2526,7 @@ void DesignPanel::on_add_thread() { bool internal = m_thread_internal->GetValue(); if (internal && m_doc.body.IsNull()) { - m_status->SetLabel(_L("Internal thread needs a body — add a solid first")); + m_status->SetLabel(_L("Thread needs a solid body — add or import one first")); return; } SketchPlane plane = thread_plane(); @@ -2556,7 +2556,7 @@ void DesignPanel::on_add_revolve() } const BooleanMode mode = static_cast(m_revolve_mode->GetSelection()); if (mode != BooleanMode::New && m_doc.body.IsNull()) { - m_status->SetLabel(_L("Add/Cut/Intersect revolve needs an existing body")); + m_status->SetLabel(_L("Revolve needs a solid body — add or import one first")); return; } m_feature_counter++; @@ -2587,7 +2587,7 @@ void DesignPanel::on_add_sweep() } const BooleanMode mode = static_cast(m_sweep_mode->GetSelection()); if (mode != BooleanMode::New && m_doc.body.IsNull()) { - m_status->SetLabel(_L("Add/Cut/Intersect sweep needs an existing body")); + m_status->SetLabel(_L("Sweep needs a solid body — add or import one first")); return; } m_feature_counter++; @@ -2615,7 +2615,7 @@ void DesignPanel::on_add_loft() } const BooleanMode mode = static_cast(m_loft_mode->GetSelection()); if (mode != BooleanMode::New && m_doc.body.IsNull()) { - m_status->SetLabel(_L("Add/Cut/Intersect loft needs an existing body")); + m_status->SetLabel(_L("Loft needs a solid body — add or import one first")); return; } m_feature_counter++; @@ -2633,7 +2633,7 @@ void DesignPanel::on_add_loft() void DesignPanel::on_add_pattern() { if (m_doc.bodies.empty()) { - m_status->SetLabel(_L("Pattern needs a body — add a solid first")); + m_status->SetLabel(_L("Pattern needs a solid body — add or import one first")); return; } const bool circular = (m_pattern_type->GetSelection() == 1); @@ -2684,7 +2684,7 @@ void DesignPanel::populate_body_choices(int as_of_feature) void DesignPanel::on_add_boolean() { if (m_doc.bodies.size() < 2) { - m_status->SetLabel(_L("Boolean needs two bodies")); + m_status->SetLabel(_L("Boolean needs two solid bodies — add or import a second one")); return; } const int sel = m_bool_op->GetSelection(); @@ -2705,7 +2705,7 @@ void DesignPanel::on_add_boolean() void DesignPanel::on_add_cut() { if (m_doc.bodies.empty()) { - m_status->SetLabel(_L("Cut needs a body")); + m_status->SetLabel(_L("Cut needs a solid body — add or import one first")); return; } m_feature_counter++; @@ -2724,7 +2724,7 @@ void DesignPanel::populate_plane_choices(wxChoice* c) const if (!c) return; const int keep = c->GetSelection(); c->Clear(); - c->Append("XY"); c->Append("XZ"); c->Append("YZ"); + c->Append(_L("XY")); c->Append(_L("XZ")); c->Append(_L("YZ")); for (const auto& dp : m_doc.resolve_datum_planes()) c->Append(wxString::FromUTF8(dp.first)); c->SetSelection((keep >= 0 && keep < int(c->GetCount())) ? keep : 0); @@ -2798,7 +2798,7 @@ void DesignPanel::on_add_plane() void DesignPanel::on_add_shell() { if (m_doc.body.IsNull()) { - m_status->SetLabel(_L("Shell needs a body — add a solid first")); + m_status->SetLabel(_L("Shell needs a solid body — add or import one first")); return; } const int face = (m_sel_solid_face >= 0) ? m_sel_solid_face : -1; @@ -2818,7 +2818,7 @@ void DesignPanel::on_add_shell() void DesignPanel::on_add_draft() { if (m_doc.body.IsNull()) { - m_status->SetLabel(_L("Draft needs a body — add a solid first")); + m_status->SetLabel(_L("Draft needs a solid body — add or import one first")); return; } if (m_sel_solid_face < 0) { @@ -5621,6 +5621,7 @@ void DesignPanel::cancel_tool() // a feature card, the Insert placement, the Sketch session, or the Constrain session. void DesignPanel::tool_confirm() { + if (m_value_cont) { confirm_value(); return; } // value card owns ribbon ✓ while a value is pending if (m_viewport && m_viewport->moving_body()) { // keep the placement, drop the gizmo m_viewport->clear_move_gizmo(); m_move_body = -1; @@ -5650,6 +5651,7 @@ void DesignPanel::tool_confirm() // drawn-but-uncommitted Sketch, or exits Constrain. void DesignPanel::tool_cancel() { + if (m_value_cont) { cancel_value(); return; } // value card owns ribbon ✗ while a value is pending if (m_viewport && m_viewport->moving_body()) { // revert to the pose at move-start sync_body_xform(); if (m_move_body >= 0 && m_move_body < int(m_body_xform.size())) diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index 07d6daf482..7004b0fc8b 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -1,7 +1,10 @@ #include "DesignSketchTool.hpp" #include "GLCanvas3D.hpp" #include "GUI_App.hpp" +#include "ImGuiWrapper.hpp" #include "Plater.hpp" + +#include #include "libslic3r/BuildVolume.hpp" #include "Camera.hpp" #include "3DScene.hpp" @@ -1882,7 +1885,12 @@ std::string DesignSketchTool::dim_text(const DimAnnot& a) const // locale at startup, so snprintf("%.1f") can emit a comma. Normalise it. for (char& ch : buf) if (ch == ',') ch = '.'; - return std::string(buf); + std::string out(buf); + if (a.kind != DimType::Angle) { + const bool use_in = wxGetApp().app_config->get_bool("use_inches"); + out += use_in ? " in" : " mm"; + } + return out; } void DesignSketchTool::apply_segment_length(double len) @@ -4898,50 +4906,43 @@ void glyph_strokes(char c, std::vector>& out, double& ad } } // namespace -void DesignSketchTool::draw_text(GLModel& model, const std::string& s, const Vec2d& center, - double height, const ColorRGBA& color) +void DesignSketchTool::draw_dim_label(const std::string& txt, const Vec2d& plane_center) { - std::vector>> glyphs; - std::vector advs; - double total = 0.0; - for (size_t i = 0; i < s.size(); ++i) { - std::vector> gs; - double adv = 0.5; - if ((unsigned char)s[i] == 0xC3 && i + 1 < s.size() && (unsigned char)s[i + 1] == 0x98) { - glyph_strokes('0', gs, adv); // 'Ø' = '0' + slash - gs.emplace_back(Vec2d(0.0, 0.0), Vec2d(0.6, 1.0)); - ++i; - } else if ((unsigned char)s[i] == 0xC2 && i + 1 < s.size() && (unsigned char)s[i + 1] == 0xB0) { - // '°' degree sign: a small open ring high in the cell, approximated by a - // short polyline loop (the stroke font has no curves primitive here). - const Vec2d c(0.18, 0.85); const double r = 0.16; - const int N = 8; Vec2d prev = c + Vec2d(r, 0); - for (int k = 1; k <= N; ++k) { - const double t = 2.0 * 3.14159265358979 * k / N; - const Vec2d cur = c + Vec2d(r * std::cos(t), r * std::sin(t)); - gs.emplace_back(prev, cur); prev = cur; - } - adv = 0.42; - ++i; - } else { - glyph_strokes(s[i], gs, adv); - } - glyphs.push_back(std::move(gs)); - advs.push_back(adv); - total += adv; - } - const Vec2d origin = center - Vec2d(total * height * 0.5, height * 0.5); - std::vector> world; - double pen = 0.0; - for (size_t g = 0; g < glyphs.size(); ++g) { - for (const auto& seg : glyphs[g]) { - const Vec2d a = origin + Vec2d((seg.first.x() + pen) * height, seg.first.y() * height); - const Vec2d b = origin + Vec2d((seg.second.x() + pen) * height, seg.second.y() * height); - world.emplace_back(a, b); - } - pen += advs[g]; - } - draw_strokes(model, world, std::max(height * 0.08, 0.02), color); + if (txt.empty()) return; + const Camera& cam = wxGetApp().plater()->get_camera(); + const wxPoint sp = world_to_screen_px(cam, m_plane.to_world(plane_center)); + if (sp.x < 0 && sp.y < 0) return; + ImGuiWrapper* imgui = wxGetApp().imgui(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(1.0f, 1.0f)); + imgui->set_next_window_pos((float)sp.x, (float)sp.y, ImGuiCond_Always, 0.5f, 0.5f); + imgui->set_next_window_bg_alpha(0.0f); + const std::string win = "##sketchdim" + std::to_string(m_dim_label_seq++); + imgui->begin(win, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration + | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoNav); + ImDrawList* dl = ImGui::GetWindowDrawList(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + const ImVec2 ts = ImGui::CalcTextSize(txt.c_str()); + const ImGuiStyle& st = ImGui::GetStyle(); + dl->AddRectFilled(ImVec2(pos.x - st.FramePadding.x, pos.y + st.FramePadding.y), + ImVec2(pos.x + ts.x + 2.0f * st.FramePadding.x, + pos.y + ts.y + 2.0f * st.FramePadding.y), + ImGuiWrapper::to_ImU32(ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f))); + ImGui::SetCursorScreenPos(ImVec2(pos.x + st.FramePadding.x, pos.y)); + imgui->text(txt); + imgui->end(); + ImGui::PopStyleVar(3); +} + +void DesignSketchTool::draw_text(GLModel& /*model*/, const std::string& s, const Vec2d& center, + double /*height*/, const ColorRGBA& /*color*/) +{ + // ponytail: all sketch labels now render as Measure-gizmo-style ImGui labels for visual + // parity with the Prepare/Preview tabs; the old vector-font path (glyph_strokes/draw_strokes + // for text) is retired. Leader lines/arrows still draw via draw_strokes at the call sites. + draw_dim_label(s, center); } // Draw every placed dimension: extension lines, the offset dimension line, arrowheads @@ -4968,21 +4969,15 @@ bool DesignSketchTool::draw_dim_quote(const DimAnnot& a, double th, const ColorR if (L < 1e-6) return false; const Vec2d u = d / L; const Vec2d nrm(-u.y(), u.x()); - const double side = (a.side != 0.0) ? a.side : 1.0; - const double off = side * std::max(L * 0.18, 8.0); - const Vec2d A2 = pa + nrm * off, B2 = pb + nrm * off; - const Vec2d ext = nrm * (off + side * 2.0); - segs.emplace_back(pa, pa + ext); // extension lines - segs.emplace_back(pb, pb + ext); - segs.emplace_back(A2, B2); // dimension line + segs.emplace_back(pa, pb); // single point-to-point dimension line const double as = std::max(L * 0.04, 2.0); auto arrow = [&](const Vec2d& tip, const Vec2d& dir) { const Vec2d back = tip + dir * as; segs.emplace_back(tip, back + nrm * (as * 0.5)); segs.emplace_back(tip, back - nrm * (as * 0.5)); }; - arrow(A2, u); arrow(B2, -u); - out_label = (A2 + B2) * 0.5 + nrm * (side * (th * 0.7 + 1.5)); + arrow(pa, u); arrow(pb, -u); + out_label = (pa + pb) * 0.5 + nrm * (th * 0.8); } else if (a.kind == DimType::Diameter || a.kind == DimType::Radius) { if (a.ea < 0 || a.ea >= int(m_entities.size())) return false; const SketchEntity& e = m_entities[a.ea]; @@ -5049,7 +5044,7 @@ bool DesignSketchTool::draw_dim_quote(const DimAnnot& a, double th, const ColorR void DesignSketchTool::render_dimensions(double unit_per_px) { if (m_dimensions.empty()) return; - const ColorRGBA dimcol(0.30f, 0.88f, 0.66f, 1.0f); // teal-green CAD quote + const ColorRGBA dimcol(0.85f, 0.85f, 0.85f, 1.0f); // neutral leader (Measure parity) // Label text is a CONSTANT screen size (like real CAD), not scaled to geometry, // so a long line doesn't get huge text. ~15 px tall in plane units at this zoom. const double th = std::max(15.0 * unit_per_px, 1e-4); @@ -6276,6 +6271,7 @@ void DesignSketchTool::confirm_transform() void DesignSketchTool::render(GLCanvas3D& canvas) { + m_dim_label_seq = 0; (void)canvas; if (!has_display()) { if (on_readout) on_readout(std::string()); // nothing to show -> hide HUD diff --git a/src/slic3r/GUI/DesignSketchTool.hpp b/src/slic3r/GUI/DesignSketchTool.hpp index 36118652ce..963c3361be 100644 --- a/src/slic3r/GUI/DesignSketchTool.hpp +++ b/src/slic3r/GUI/DesignSketchTool.hpp @@ -570,6 +570,7 @@ private: double hw, const ColorRGBA& color); void draw_text(GLModel& model, const std::string& s, const Vec2d& center, double height, const ColorRGBA& color); // GL stroke font + void draw_dim_label(const std::string& txt, const Vec2d& plane_center); // Entity builders: append to m_entities (honoring the construction flag). void push_line(const Vec2d& a, const Vec2d& b); @@ -817,6 +818,7 @@ private: GLModel m_line_model; GLModel m_vertex_model; GLModel m_highlight_model; + int m_dim_label_seq{0}; GLModel m_fill_model; // translucent face fill for closed regions std::vector m_display_sketches; // committed sketches drawn persistently int m_display_pick{-1}; // FEATURE index of the click-selected display sketch (-1 none)