From 9340d4c7dc9825104a429de7d80149f27a99ed47 Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Fri, 14 Aug 2026 08:36:19 +0200 Subject: [PATCH] CAD: the DoF readout comes back in Constrain mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leaving Sketch clears the "N degrees of freedom" line, which is right — it describes a sketch's constraint state and means nothing in Feature mode. But entering CONSTRAIN left it blank too, and Constrain is where the number is the whole point: the readout is fed only by a live solve, and no solve fires merely because the mode changed, so the line stayed empty until the user happened to change something. The solve callback now caches its last result and the labelling is split into apply_dof_status(), which set_ui_mode re-applies on entry to Constrain. Verified on the rig: a rectangle reports "4 degrees of freedom" in Sketch, and the same line is there after K enters Constrain. --- src/slic3r/GUI/DesignPanel.cpp | 50 ++++++++++++++++++++++------------ src/slic3r/GUI/DesignPanel.hpp | 5 ++++ 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index 865ea29eab..1428db0ca0 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -3291,23 +3291,11 @@ DesignPanel::DesignPanel(wxWindow* parent) // DoF feedback (P3): after each live solve, report constraint state on its own // line. Green = fully constrained, red = conflicting, neutral = N remaining DoF. m_viewport->set_on_solve_state([this](int dof, bool ok, bool has_constraints) { - if (!m_dof_status) return; - if (!has_constraints) { - m_dof_status->SetLabel(wxString()); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty()); - } else if (!ok) { - m_dof_status->SetForegroundColour(wxColour(235, 80, 80)); - m_dof_status->SetLabel(_L("✗ Conflicting constraints")); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty()); - } else if (dof == 0) { - m_dof_status->SetForegroundColour(wxColour(80, 200, 110)); - m_dof_status->SetLabel(_L("✓ Fully constrained")); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty()); - } else if (dof > 0) { - m_dof_status->SetForegroundColour(dp_ctl_text()); - m_dof_status->SetLabel(wxString::Format(_L("%d degrees of freedom"), dof)); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty()); - } else { - m_dof_status->SetLabel(wxString()); m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty()); - } - m_dof_status->Refresh(); - update_cards_frame(); m_form->Layout(); + // Remembered as well as shown: the readout is fed ONLY by a live solve, and entering + // Constrain mode triggers none — so without a cache the very line the user goes to + // Constrain to read stays blank until they happen to change something. + m_dof_last = dof; m_dof_last_ok = ok; m_dof_last_has = has_constraints; + apply_dof_status(dof, ok, has_constraints); }); // Selection (Select tool): reflect the count in the status line. @@ -3951,6 +3939,30 @@ void DesignPanel::set_active_tool_btn(ScalableButton* b) } } +// Paint the DoF line. Split out of the solve callback so entering Constrain can re-apply the +// last known state — see m_dof_last. +void DesignPanel::apply_dof_status(int dof, bool ok, bool has_constraints) +{ + if (!m_dof_status) return; + if (!has_constraints) { + m_dof_status->SetLabel(wxString()); + } else if (!ok) { + m_dof_status->SetForegroundColour(wxColour(235, 80, 80)); + m_dof_status->SetLabel(_L("✗ Conflicting constraints")); + } else if (dof == 0) { + m_dof_status->SetForegroundColour(wxColour(80, 200, 110)); + m_dof_status->SetLabel(_L("✓ Fully constrained")); + } else if (dof > 0) { + m_dof_status->SetForegroundColour(dp_ctl_text()); + m_dof_status->SetLabel(wxString::Format(_L("%d degrees of freedom"), dof)); + } else { + m_dof_status->SetLabel(wxString()); + } + m_dof_status->Show(!m_dof_status->GetLabel().IsEmpty()); + m_dof_status->Refresh(); + update_cards_frame(); m_form->Layout(); +} + void DesignPanel::set_ui_mode(UiMode m) { m_ui_mode = m; @@ -3964,6 +3976,10 @@ void DesignPanel::set_ui_mode(UiMode m) m_dof_status->SetLabel(wxString()); m_dof_status->Show(false); } + // Constrain is where the number is the whole point, and clearing it on the way out of + // Sketch left it blank on the way in — no solve fires merely because the mode changed. + if (m == UiMode::Constrain) + apply_dof_status(m_dof_last, m_dof_last_ok, m_dof_last_has); wxSizer* s = m_toolbar->GetSizer(); s->Show(m_tb_feature, m == UiMode::Feature, true); s->Show(m_tb_sketch, m == UiMode::Sketch, true); diff --git a/src/slic3r/GUI/DesignPanel.hpp b/src/slic3r/GUI/DesignPanel.hpp index 76f2d61e00..29345e472f 100644 --- a/src/slic3r/GUI/DesignPanel.hpp +++ b/src/slic3r/GUI/DesignPanel.hpp @@ -79,6 +79,7 @@ private: // Constrain = constraints + edit ops). Replaces the old always-visible wall. enum class UiMode { Feature, Sketch, Constrain }; void set_ui_mode(UiMode m); + void apply_dof_status(int dof, bool ok, bool has_constraints); // Unified action-bar dispatch: one Confirm / one Cancel for every tool and mode. void tool_confirm(); // ✓ : commit the active feature / sketch / constrain session void tool_cancel(); // ✗ / Esc : cancel the active feature / discard / exit @@ -822,6 +823,10 @@ private: // reliable way to tell a chosen colour (the error red) from the default. See set_status(). wxColour m_status_default_fg; wxStaticText* m_dof_status{nullptr}; // DoF / constraint-state readout (P3) + // Last live-solve result, so entering Constrain can restore the readout without a solve. + int m_dof_last{-1}; + bool m_dof_last_ok{true}; + bool m_dof_last_has{false}; int m_feature_counter{0}; std::vector m_confirm_btns;