From 49c5822be4ea6ee31ddae0294966673bfce0f894 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 4 Feb 2020 14:53:17 +0100 Subject: [PATCH 01/16] Show tooltip for all hovered ticks, not just for a selected one --- src/slic3r/GUI/DoubleSlider.cpp | 163 +++++++++++++++++--------------- src/slic3r/GUI/DoubleSlider.hpp | 14 ++- 2 files changed, 98 insertions(+), 79 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 5990984b0..59c7de8ea 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -875,87 +875,90 @@ void Control::correct_higher_value() m_lower_value = m_higher_value; } -wxString Control::get_tooltip(IconFocus icon_focus) +wxString Control::get_tooltip(FocusItem focused_item, int tick/*=-1*/) { - wxString tooltip(wxEmptyString); - if (m_is_one_layer_icon_focesed) - tooltip = _(L("One layer mode")); + if (focused_item == fiNone) + return ""; + if (focused_item == fiOneLayerIcon) + return _(L("One layer mode")); + if (focused_item == fiRevertIcon) + return _(L("Discard all custom changes")); + if (focused_item == fiCogIcon) + return _(L("Set extruder sequence for whole print")); + if (focused_item == fiColorBand) + return m_mode != t_mode::SingleExtruder ? "" : + _(L("For edit current color use Right(Double) mouse click on colored band")); - if (icon_focus == ifRevert) - tooltip = _(L("Discard all custom changes")); - if (icon_focus == ifCog) - tooltip = _(L("Set extruder sequence for whole print")); - else if (m_is_action_icon_focesed) + wxString tooltip; + const auto tick_code_it = m_ticks.ticks.find(TickCode{tick}); + + if (tick_code_it == m_ticks.ticks.end() && focused_item == fiActionIcon) // tick doesn't exist { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - const auto tick_code_it = m_ticks.ticks.find(TickCode{tick}); + // Show mode as a first string of tooltop + tooltip = " " + _(L("Slider(print) mode")) + ": "; + tooltip += (m_mode == t_mode::SingleExtruder ? CustomGCode::SingleExtruderMode : + m_mode == t_mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode : + CustomGCode::MultiExtruderMode ); + tooltip += "\n\n"; /* Note: just on OSX!!! - * Right click event causes a little scrolling. + * Right click event causes a little scrolling. * So, as a workaround we use Ctrl+LeftMouseClick instead of RightMouseClick * Show this information in tooltip * */ - if (tick_code_it == m_ticks.ticks.end()) // tick doesn't exist - { - // Show mode as a first string of tooltop - tooltip = " " + _(L("Slider(print) mode")) + ": "; - tooltip += (m_mode == t_mode::SingleExtruder ? CustomGCode::SingleExtruderMode : - m_mode == t_mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode : - CustomGCode::MultiExtruderMode ); - tooltip += "\n\n"; - - // Show list of actions with new tick - tooltip += ( m_mode == t_mode::MultiAsSingle ? - _(L("For add change extruder use left mouse button click")) : - _(L("For add color change use left mouse button click")) ) + " " + - _(L("OR pres \"+\" key")) + "\n" + ( - is_osx ? - _(L("For add another code use Ctrl + Left mouse button click")) : - _(L("For add another code use right mouse button click")) ); - } - else // tick exists - { - // Show custom Gcode as a first string of tooltop - tooltip = " "; - tooltip += tick_code_it->gcode == ColorChangeCode ? ( - m_mode == t_mode::SingleExtruder ? - from_u8((boost::format(_utf8(L("Color change (\"%1%\")"))) % tick_code_it->gcode ).str()) : - from_u8((boost::format(_utf8(L("Color change (\"%1%\") for Extruder %2%"))) % - tick_code_it->gcode % tick_code_it->extruder).str()) ) : - tick_code_it->gcode == PausePrintCode ? - from_u8((boost::format(_utf8(L("Pause print (\"%1%\")"))) % tick_code_it->gcode ).str()) : - tick_code_it->gcode == ToolChangeCode ? - from_u8((boost::format(_utf8(L("Extruder(tool) is changed to Extruder \"%1%\""))) % tick_code_it->extruder ).str()) : - from_u8((boost::format(_utf8(L("\"%1%\""))) % tick_code_it->gcode ).str()) ; - - // If tick is marked as a conflict (exclamation icon), - // we should to explain why - ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]); - if (conflict != ctNone) - tooltip += "\n\n" + _(L("Note")) + "! "; - if (conflict == ctModeConflict) - tooltip += _(L("G-code of this tick has a conflict with slider(print) mode.\n" - "Any its editing will cause a changes of DoubleSlider data.")); - else if (conflict == ctMeaninglessColorChange) - tooltip += _(L("There is a color change for extruder that wouldn't be used till the end of printing.\n" - "This code wouldn't be processed during GCode generation.")); - else if (conflict == ctMeaninglessToolChange) - tooltip += _(L("There is a extruder change to the same extruder.\n" - "This code wouldn't be processed during GCode generation.")); - else if (conflict == ctRedundant) - tooltip += _(L("There is a color change for extruder that has not been used before.\n" - "Check your choice to avoid redundant color changes.")); - - // Show list of actions with existing tick - tooltip += "\n\n" + _(L("For Delete tick use left mouse button click OR pres \"-\" key")) + "\n" + ( - is_osx ? - _(L("For Edit tick use Ctrl + Left mouse button click")) : - _(L("For Edit tick use right mouse button click")) ); - } + // Show list of actions with new tick + tooltip += ( m_mode == t_mode::MultiAsSingle ? + _(L("For add change extruder use left mouse button click")) : + _(L("For add color change use left mouse button click")) ) + " " + + _(L("OR pres \"+\" key")) + "\n" + ( + is_osx ? + _(L("For add another code use Ctrl + Left mouse button click")) : + _(L("For add another code use right mouse button click")) ); } + if (tick_code_it != m_ticks.ticks.end()) // tick exists + { + // Show custom Gcode as a first string of tooltop + tooltip = " "; + tooltip += tick_code_it->gcode == ColorChangeCode ? ( + m_mode == t_mode::SingleExtruder ? + from_u8((boost::format(_utf8(L("Color change (\"%1%\")"))) % tick_code_it->gcode ).str()) : + from_u8((boost::format(_utf8(L("Color change (\"%1%\") for Extruder %2%"))) % + tick_code_it->gcode % tick_code_it->extruder).str()) ) : + tick_code_it->gcode == PausePrintCode ? + from_u8((boost::format(_utf8(L("Pause print (\"%1%\")"))) % tick_code_it->gcode ).str()) : + tick_code_it->gcode == ToolChangeCode ? + from_u8((boost::format(_utf8(L("Extruder(tool) is changed to Extruder \"%1%\""))) % tick_code_it->extruder ).str()) : + from_u8((boost::format(_utf8(L("\"%1%\""))) % tick_code_it->gcode ).str()) ; + + // If tick is marked as a conflict (exclamation icon), + // we should to explain why + ConflictType conflict = m_ticks.is_conflict_tick(*tick_code_it, m_mode, m_only_extruder, m_values[tick]); + if (conflict != ctNone) + tooltip += "\n\n" + _(L("Note")) + "! "; + if (conflict == ctModeConflict) + tooltip += _(L("G-code of this tick has a conflict with slider(print) mode.\n" + "Any its editing will cause a changes of DoubleSlider data.")); + else if (conflict == ctMeaninglessColorChange) + tooltip += _(L("There is a color change for extruder that wouldn't be used till the end of printing.\n" + "This code wouldn't be processed during GCode generation.")); + else if (conflict == ctMeaninglessToolChange) + tooltip += _(L("There is a extruder change to the same extruder.\n" + "This code wouldn't be processed during GCode generation.")); + else if (conflict == ctRedundant) + tooltip += _(L("There is a color change for extruder that has not been used before.\n" + "Check your choice to avoid redundant color changes.")); + + // Show list of actions with existing tick + if (focused_item == fiActionIcon) + tooltip += "\n\n" + _(L("For Delete tick use left mouse button click OR pres \"-\" key")) + "\n" + ( + is_osx ? + _(L("For Edit tick use Ctrl + Left mouse button click")) : + _(L("For Edit tick use right mouse button click")) ); + } return tooltip; + } void Control::OnMotion(wxMouseEvent& event) @@ -966,14 +969,26 @@ void Control::OnMotion(wxMouseEvent& event) const wxPoint pos = event.GetLogicalPosition(dc); m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - IconFocus icon_focus = ifNone; + + FocusItem focused_item = fiNone; + int tick = -1; if (!m_is_left_down && !m_is_one_layer) { m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); - if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) - icon_focus = ifRevert; + if (m_is_one_layer_icon_focesed) + focused_item = fiOneLayerIcon; + else if (m_is_action_icon_focesed) { + focused_item = fiActionIcon; + tick = m_selection == ssLower ? m_lower_value : m_higher_value; + } + else if (!m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon)) + focused_item = fiRevertIcon; else if (is_point_in_rect(pos, m_rect_cog_icon)) - icon_focus = ifCog; + focused_item = fiCogIcon; + else { + focused_item = fiTick; + tick = get_tick_near_point(pos); + } } else if (m_is_left_down || m_is_right_down) { if (m_selection == ssLower) { @@ -994,7 +1009,7 @@ void Control::OnMotion(wxMouseEvent& event) event.Skip(); // Set tooltips with information for each icon - this->SetToolTip(get_tooltip(icon_focus)); + this->SetToolTip(get_tooltip(focused_item, tick)); if (action) { diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 01181fc2f..00f64a6f6 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -33,10 +33,14 @@ enum SelectedSlider { ssHigher }; -enum IconFocus { - ifNone, - ifRevert, - ifCog +enum FocusItem { + fiNone, + fiRevertIcon, + fiOneLayerIcon, + fiCogIcon, + fiColorBand, + fiActionIcon, + fiTick }; enum ConflictType @@ -263,7 +267,7 @@ private: wxSize get_size(); void get_size(int *w, int *h); double get_double_value(const SelectedSlider& selection); - wxString get_tooltip(IconFocus icon_focus); + wxString get_tooltip(FocusItem focused_item, int tick = -1); std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; std::string get_color_for_color_change_tick(std::set::const_iterator it) const; From 58b5e75a9c1ea3ae28b1c1f767e0024cfb9bdf6b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 4 Feb 2020 12:18:57 +0100 Subject: [PATCH 02/16] SlaSupports gizmo - show holes position even when user did not Preview the drilled mesh --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 3 +++ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 8 ++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index af022352e..c237198a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -358,12 +358,14 @@ bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* mode m_backend_mesh_transformed = po->get_mesh_to_print(); m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse()); m_mesh = &m_backend_mesh_transformed; + m_has_drilled_mesh = true; } } if (! m_mesh) { m_mesh = &m_model_object->volumes.front()->mesh(); m_backend_mesh_transformed.clear(); + m_has_drilled_mesh = false; } m_model_object_id = m_model_object->id(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 5cd3d9d84..f3941b0a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -227,10 +227,13 @@ public: m_clipping_plane_distance = m_clipping_plane_distance_stash; } + bool has_drilled_mesh() const { return m_has_drilled_mesh; } + private: const TriangleMesh* m_old_mesh; TriangleMesh m_backend_mesh_transformed; float m_clipping_plane_distance_stash = 0.f; + bool m_has_drilled_mesh = false; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 879a09da2..fd9b116a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -335,7 +335,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } // Now render the drain holes: - /*if (! m_c->m_cavity_mesh) { + if (! m_c->has_drilled_mesh()) { render_color[0] = 0.7f; render_color[1] = 0.7f; render_color[2] = 0.7f; @@ -370,7 +370,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glFrontFace(GL_CCW); glsafe(::glPopMatrix()); } - }*/ + } if (!picking) glsafe(::glDisable(GL_LIGHTING)); @@ -414,14 +414,14 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairm_cavity_mesh) { + if (! m_c->has_drilled_mesh()) { for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { if (hole.is_inside(hit)) { in_hole = true; break; } } - }*/ + } if (! in_hole) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); From 808cf2e38a5b8aeb9af4a09c80a80dd7563ce0fd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 4 Feb 2020 13:02:58 +0100 Subject: [PATCH 03/16] Hollowing: merge meshes of holes with igl (intersecting holes) --- src/libslic3r/SLAPrintSteps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index b7d1cfa1d..7810a351f 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -143,7 +143,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) holes_mesh.merge(sla::to_triangle_mesh(holept.to_mesh())); holes_mesh.require_shared_vertices(); - MeshBoolean::cgal::self_union(holes_mesh); //FIXME-fix and use the cgal version + MeshBoolean::self_union(holes_mesh); try { MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh); From 0e3ebb3e0777407f28e9559c538238024439c682 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 4 Feb 2020 13:03:28 +0100 Subject: [PATCH 04/16] Hollowing gizmo: Invalidate drilled mesh in case a hole is manipulated This way the holes cannot be placed on its own walls Downside is that AABB trees have to be recalculated --- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 15 +++++++++++++++ src/slic3r/GUI/Plater.cpp | 1 + 4 files changed, 18 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e12e8c661..0741c47cf 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1241,6 +1241,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a0e795889..39dd2d744 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -91,6 +91,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index b585a8e4f..e3b18792e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -330,6 +330,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairm_mesh_raycaster) return false; + // if the gizmo doesn't have the V, F structures for igl, calculate them first: // !!! is it really necessary? //m_c->update_from_backend(m_parent, m_c->m_model_object); @@ -344,6 +345,18 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairm_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) { + + // User is about to manipulate a hole. If the gizmo currently shows drilled mesh, + // invalidate slaposDrillHoles so it returns to normal. To do this, hackishly + // add a hole, force SLAPrint::apply call that will invalidate the step because + // of it and then remove the hole again. + if (m_c->has_drilled_mesh()) { + m_c->m_model_object->sla_drain_holes.push_back(sla::DrainHole()); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); + wxGetApp().CallAfter([this] { m_c->m_model_object->sla_drain_holes.pop_back();}); + return false; + } + // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; @@ -513,6 +526,8 @@ void GLGizmoHollow::delete_selected_points() } } + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); + select_point(NoPoints); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bbb7db74d..7f615b7f6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2062,6 +2062,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); + view3D_canvas->Bind(EVT_GLCANVAS_FORCE_UPDATE, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); From 617912ecc1c380707fbfe5f9349af64d32b196d9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 4 Feb 2020 15:01:23 +0100 Subject: [PATCH 05/16] Fixed a crash on deleting object with SLA gizmo active Rephrased text informing about hidden objects in both SLA gizmos --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0741c47cf..16fe7696c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -683,7 +683,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool case ObjectOutside : text = L("An object outside the print area was detected"); break; case ToolpathOutside : text = L("A toolpath outside the print area was detected"); break; case SlaSupportsOutside : text = L("SLA supports outside the print area were detected"); break; - case SomethingNotShown : text = L("Some objects are not visible when editing supports"); break; + case SomethingNotShown : text = L("Some objects are not visible"); break; case ObjectClashed: { text = L("An object outside the print area was detected\n" "Resolve the current problem to continue slicing"); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fd9b116a9..5130b7a9a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -1291,6 +1291,8 @@ bool GLGizmoSlaSupports::unsaved_changes() const void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const { + if (! m_c->m_model_object) + return; Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); From 65b9ef6636dbee66aeced9b4d0c7f6288e532bfd Mon Sep 17 00:00:00 2001 From: David Kocik Date: Tue, 4 Feb 2020 15:24:35 +0100 Subject: [PATCH 06/16] configuration updater forced update dialog check for updates button check address when downloading bundles --- src/slic3r/Config/Version.cpp | 4 ++ src/slic3r/Config/Version.hpp | 1 + src/slic3r/GUI/GUI_App.cpp | 66 +++++++++++++--------- src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/Plater.cpp | 7 +++ src/slic3r/GUI/Plater.hpp | 2 + src/slic3r/GUI/UpdateDialogs.cpp | 90 +++++++++++++++++++++++++++++- src/slic3r/GUI/UpdateDialogs.hpp | 38 +++++++++++++ src/slic3r/Utils/PresetUpdater.cpp | 87 ++++++++++++++++++++++++----- 9 files changed, 257 insertions(+), 39 deletions(-) diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 2104a6eea..2d036e9f3 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -66,6 +66,10 @@ bool Version::is_current_slic3r_supported() const return this->is_slic3r_supported(Slic3r::SEMVER); } +bool Version::is_current_slic3r_downgrade() const +{ + return Slic3r::SEMVER < min_slic3r_version; +} #if 0 //TODO: This test should be moved to a unit test, once we have C++ unit tests in place. static int version_test() diff --git a/src/slic3r/Config/Version.hpp b/src/slic3r/Config/Version.hpp index e5f1a2e21..881b856ff 100644 --- a/src/slic3r/Config/Version.hpp +++ b/src/slic3r/Config/Version.hpp @@ -29,6 +29,7 @@ struct Version bool is_slic3r_supported(const Semver &slicer_version) const; bool is_current_slic3r_supported() const; + bool is_current_slic3r_downgrade() const; }; // Index of vendor specific config bundle versions and Slic3r compatibilities. diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c6f5cfb47..3a097d9ab 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -278,34 +278,23 @@ bool GUI_App::on_init_inner() RemovableDriveManager::get_instance().update(wxGetLocalTime(), true); #endif - // Preset updating & Configwizard are done after the above initializations, - // and after MainFrame is created & shown. - // The extra CallAfter() is needed because of Mac, where this is the only way - // to popup a modal dialog on start without screwing combo boxes. - // This is ugly but I honestly found no better way to do it. - // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + // Preset updating & Configwizard are done after the above initializations, + // and after MainFrame is created & shown. + // The extra CallAfter() is needed because of Mac, where this is the only way + // to popup a modal dialog on start without screwing combo boxes. + // This is ugly but I honestly found no better way to do it. + // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + static bool once = true; if (once) { once = false; + check_updates(false); - PresetUpdater::UpdateResult updater_result; - try { - updater_result = preset_updater->config_update(app_config->orig_version()); - if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { - mainframe->Close(); - } else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { - app_conf_exists = true; - } - } catch (const std::exception &ex) { - show_error(nullptr, from_u8(ex.what())); - } - - CallAfter([this] { - config_wizard_startup(); - preset_updater->slic3r_update_notify(); - preset_updater->sync(preset_bundle); - }); - + CallAfter([this] { + config_wizard_startup(); + preset_updater->slic3r_update_notify(); + preset_updater->sync(preset_bundle); + }); } }); @@ -810,7 +799,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("&Configuration Snapshots")) + dots, _(L("Inspect / activate configuration snapshots"))); local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration &Snapshot")), _(L("Capture a configuration snapshot"))); - // local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); + local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); local_menu->AppendSeparator(); local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("&Preferences")) + dots + #ifdef __APPLE__ @@ -841,6 +830,9 @@ void GUI_App::add_config_menu(wxMenuBar *menu) case ConfigMenuWizard: run_wizard(ConfigWizard::RR_USER); break; + case ConfigMenuUpdate: + check_updates(true); + break; case ConfigMenuTakeSnapshot: // Take a configuration snapshot. if (check_unsaved_changes()) { @@ -1230,6 +1222,30 @@ bool GUI_App::config_wizard_startup() return false; } +void GUI_App::check_updates(const bool verbose) +{ + + PresetUpdater::UpdateResult updater_result; + try { + updater_result = preset_updater->config_update(app_config->orig_version()); + if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) { + mainframe->Close(); + } + else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) { + app_conf_exists = true; + } + else if(verbose && updater_result == PresetUpdater::R_NOOP) + { + MsgNoUpdates dlg; + dlg.ShowModal(); + } + } + catch (const std::exception & ex) { + show_error(nullptr, from_u8(ex.what())); + } + + +} // static method accepting a wxWindow object as first parameter // void warning_catcher{ // my($self, $message_dialog) = @_; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 250d8122a..10b09b1da 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -200,6 +200,7 @@ private: bool select_language(); bool config_wizard_startup(); + void check_updates(const bool verbose); #ifdef __WXMSW__ void associate_3mf_files(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7f615b7f6..1b69decfc 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5375,6 +5375,13 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->p->schedule_background_process(); } +void Plater::set_bed_shape() const +{ + p->set_bed_shape(p->config->option("bed_shape")->values, + p->config->option("bed_custom_texture")->value, + p->config->option("bed_custom_model")->value); +} + void Plater::force_filament_colors_update() { bool update_scheduled = false; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 568727abf..e9d71b299 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -284,6 +284,8 @@ public: const Mouse3DController& get_mouse3d_controller() const; Mouse3DController& get_mouse3d_controller(); + void set_bed_shape() const; + // ROII wrapper for suppressing the Undo / Redo snapshot to be taken. class SuppressSnapshots { diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index d5c69be0b..b152ed135 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -142,6 +142,71 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector &updates) : MsgUpdateConfig::~MsgUpdateConfig() {} +//MsgUpdateForced + +MsgUpdateForced::MsgUpdateForced(const std::vector& updates) : + MsgDialog(nullptr, wxString::Format(_(L("%s incompatibility")), SLIC3R_APP_NAME), _(L("Configuration update is necessary to install")), wxID_NONE) +{ + auto* text = new wxStaticText(this, wxID_ANY, wxString::Format(_(L( + "%s will now start updates. Otherwise it won't be able to start.\n\n" + "Note that a full configuration snapshot will be created first. It can then be restored at any time " + "should there be a problem with the new version.\n\n" + "Updated configuration bundles:" + )), SLIC3R_APP_NAME)); + + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); + + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + const auto lang_code = wxGetApp().current_language_code_safe().ToStdString(); + + auto* versions = new wxBoxSizer(wxVERTICAL); + for (const auto& update : updates) { + auto* flex = new wxFlexGridSizer(2, 0, VERT_SPACING); + + auto* text_vendor = new wxStaticText(this, wxID_ANY, update.vendor); + text_vendor->SetFont(boldfont); + flex->Add(text_vendor); + flex->Add(new wxStaticText(this, wxID_ANY, update.version.to_string())); + + if (!update.comment.empty()) { + flex->Add(new wxStaticText(this, wxID_ANY, _(L("Comment:"))), 0, wxALIGN_RIGHT); + auto* update_comment = new wxStaticText(this, wxID_ANY, from_u8(update.comment)); + update_comment->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + flex->Add(update_comment); + } + + versions->Add(flex); + + if (!update.changelog_url.empty() && update.version.prerelease() == nullptr) { + auto* line = new wxBoxSizer(wxHORIZONTAL); + auto changelog_url = (boost::format(update.changelog_url) % lang_code).str(); + line->AddSpacer(3 * VERT_SPACING); + line->Add(new wxHyperlinkCtrl(this, wxID_ANY, _(L("Open changelog page")), changelog_url)); + versions->Add(line); + } + } + + content_sizer->Add(versions); + content_sizer->AddSpacer(2 * VERT_SPACING); + + auto* btn_exit = new wxButton(this, wxID_EXIT, wxString::Format(_(L("Exit %s")), SLIC3R_APP_NAME)); + btn_sizer->Add(btn_exit); + btn_sizer->AddSpacer(HORIZ_SPACING); + auto* btn_ok = new wxButton(this, wxID_OK); + btn_sizer->Add(btn_ok); + btn_ok->SetFocus(); + + auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); }; + btn_exit->Bind(wxEVT_BUTTON, exiter); + btn_ok->Bind(wxEVT_BUTTON, exiter); + + Fit(); +} + +MsgUpdateForced::~MsgUpdateForced() {} // MsgDataIncompatible @@ -157,7 +222,7 @@ MsgDataIncompatible::MsgDataIncompatible(const std::unordered_mapWrap(CONTENT_WIDTH * wxGetApp().em_unit()); content_sizer->Add(text); @@ -236,5 +301,28 @@ MsgDataLegacy::MsgDataLegacy() : MsgDataLegacy::~MsgDataLegacy() {} +// MsgNoUpdate + +MsgNoUpdates::MsgNoUpdates() : + MsgDialog(nullptr, _(L("Configuration updates")), _(L("No updates aviable"))) +{ + + auto* text = new wxStaticText(this, wxID_ANY, wxString::Format( + _(L( + "%s has no configuration updates aviable." + )), + SLIC3R_APP_NAME, ConfigWizard::name() + )); + text->Wrap(CONTENT_WIDTH * wxGetApp().em_unit()); + content_sizer->Add(text); + content_sizer->AddSpacer(VERT_SPACING); + + logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192)); + + Fit(); +} + +MsgNoUpdates::~MsgNoUpdates() {} + } } diff --git a/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp index a916e0145..6d355065a 100644 --- a/src/slic3r/GUI/UpdateDialogs.hpp +++ b/src/slic3r/GUI/UpdateDialogs.hpp @@ -62,6 +62,33 @@ public: ~MsgUpdateConfig(); }; +// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. +class MsgUpdateForced : public MsgDialog +{ +public: + struct Update + { + std::string vendor; + Semver version; + std::string comment; + std::string changelog_url; + + Update(std::string vendor, Semver version, std::string comment, std::string changelog_url) + : vendor(std::move(vendor)) + , version(std::move(version)) + , comment(std::move(comment)) + , changelog_url(std::move(changelog_url)) + {} + }; + + MsgUpdateForced(const std::vector& updates); + MsgUpdateForced(MsgUpdateForced&&) = delete; + MsgUpdateForced(const MsgUpdateForced&) = delete; + MsgUpdateForced& operator=(MsgUpdateForced&&) = delete; + MsgUpdateForced& operator=(const MsgUpdateForced&) = delete; + ~MsgUpdateForced(); +}; + // Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. class MsgDataIncompatible : public MsgDialog { @@ -87,6 +114,17 @@ public: ~MsgDataLegacy(); }; +// Informs about absence of bundles requiring update. +class MsgNoUpdates : public MsgDialog +{ +public: + MsgNoUpdates(); + MsgNoUpdates(MsgNoUpdates&&) = delete; + MsgNoUpdates(const MsgNoUpdates&) = delete; + MsgNoUpdates& operator=(MsgNoUpdates&&) = delete; + MsgNoUpdates& operator=(const MsgNoUpdates&) = delete; + ~MsgNoUpdates(); +}; } } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 55e3a5a73..ecdc12b50 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -73,13 +73,16 @@ struct Update std::string vendor; std::string changelog_url; + bool forced_update; + Update() {} - Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url) + Update(fs::path &&source, fs::path &&target, const Version &version, std::string vendor, std::string changelog_url, bool forced = false) : source(std::move(source)) , target(std::move(target)) , version(version) , vendor(std::move(vendor)) , changelog_url(std::move(changelog_url)) + , forced_update(forced) {} void install() const @@ -297,6 +300,12 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors) const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME; const std::string idx_path = (cache_path / (vendor.id + ".idx")).string(); const std::string idx_path_temp = idx_path + "-update"; + //check if idx_url is leading to our site + if(idx_url.substr(0, 54) != "http://files.prusa3d.com/wp-content/uploads/repository") + { + BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor: " << vendor.name; + continue; + } if (!get_file(idx_url, idx_path_temp)) { continue; } if (cancel) { return; } @@ -418,11 +427,16 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version continue; } - if (ver_current_found && !ver_current->is_current_slic3r_supported()) { - // "Reconfigure" situation. - BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); - updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); - continue; + bool current_not_supported = false; //if slcr is incompatible but situation is not downgrade, we do forced updated and this bool is information to do it + + if (ver_current_found && !ver_current->is_current_slic3r_supported()){ + if(ver_current->is_current_slic3r_downgrade()) { + // "Reconfigure" situation. + BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); + updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); + continue; + } + current_not_supported = true; } if (recommended->config_version < vp.config_version) { @@ -462,7 +476,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version if (new_vp.config_version == recommended->config_version) { // The config bundle from the cache directory matches the recommended version of the index from the cache directory. // This is the newest known recommended config. Use it. - new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url); + new_update = Update(std::move(path_in_cache), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported); // and install the config index from the cache into vendor's directory. bundle_path_idx_to_install = idx.path(); found = true; @@ -492,7 +506,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version } recommended = rsrc_idx.recommended(); if (recommended != rsrc_idx.end() && recommended->config_version == rsrc_vp.config_version && recommended->config_version > vp.config_version) { - new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url); + new_update = Update(std::move(path_in_rsrc), std::move(bundle_path), *recommended, vp.name, vp.changelog_url, current_not_supported); bundle_path_idx_to_install = path_idx_in_rsrc; found = true; } else { @@ -513,11 +527,11 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version // Find a recommended config bundle version for the slic3r version last executed. This makes sure that a config bundle update will not be missed // when upgrading an application. On the other side, the user will be bugged every time he will switch between slic3r versions. const auto existing_recommended = existing_idx.recommended(old_slic3r_version); - if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) { + /*if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) { // The user has already seen (and presumably rejected) this update BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor(); continue; - } + }*/ } catch (const std::exception &err) { BOOST_LOG_TRIVIAL(error) << boost::format("Cannot load the installed index at `%1%`: %2%") % bundle_path_idx % err.what(); } @@ -554,14 +568,17 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE); } - + BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size(); for (auto &incompat : updates.incompats) { BOOST_LOG_TRIVIAL(info) << '\t' << incompat; incompat.remove(); } + + } else if (updates.updates.size() > 0) { + if (snapshot) { BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE); @@ -688,6 +705,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 ); } else if (min_slic3r != Semver::zero()) { restrictions = wxString::Format(_(L("requires min. %s")), min_slic3r.to_string()); + BOOST_LOG_TRIVIAL(debug) << "Bundle is not downgrade, user will now have to do whole wizard. This should not happen."; } else { restrictions = wxString::Format(_(L("requires max. %s")), max_slic3r.to_string()); } @@ -704,16 +722,59 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver &old_slic3 // (snapshot is taken beforehand) p->perform_updates(std::move(updates)); - if (! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { + if (!GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) { return R_INCOMPAT_EXIT; } return R_INCOMPAT_CONFIGURED; - } else { + } + else { BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; return R_INCOMPAT_EXIT; } + } else if (updates.updates.size() > 0) { + + bool incompatible_version = false; + for (const auto& update : updates.updates) { + incompatible_version = (update.forced_update ? true : incompatible_version); + //td::cout << update.forced_update << std::endl; + //BOOST_LOG_TRIVIAL(info) << boost::format("Update requires higher version."); + } + + //forced update + if(incompatible_version) + { + BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. At least one requires higher version of Slicer.") % updates.updates.size(); + + std::vector updates_msg; + for (const auto& update : updates.updates) { + std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string(); + updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url)); + } + + GUI::MsgUpdateForced dlg(updates_msg); + + const auto res = dlg.ShowModal(); + if (res == wxID_OK) { + BOOST_LOG_TRIVIAL(info) << "User wants to update..."; + + p->perform_updates(std::move(updates)); + + // Reload global configuration + auto* app_config = GUI::wxGetApp().app_config; + GUI::wxGetApp().preset_bundle->load_presets(*app_config); + GUI::wxGetApp().load_current_presets(); + GUI::wxGetApp().plater()->set_bed_shape(); + return R_UPDATE_INSTALLED; + } + else { + BOOST_LOG_TRIVIAL(info) << "User wants to exit Slic3r, bye..."; + return R_INCOMPAT_EXIT; + } + } + + // regular update BOOST_LOG_TRIVIAL(info) << boost::format("Update of %1% bundles available. Asking for confirmation ...") % updates.updates.size(); std::vector updates_msg; From 059bdb4711e6ca392c707b70ab6d450a96bb85c9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Feb 2020 14:43:58 +0100 Subject: [PATCH 07/16] Implemented handling of complex ConfigOptionFloatOrPercent chains by the PlaceholderParser. Namely, all the options with the "ratio_over" reference are now handled correctly by the PlaceholderParser with the exception of the "first_layer_extrusion_width", which overrides speed of extrusions by their respective extrusion type. Also the various extrusion widths (extrusion_width, first_layer_extrusion_width, external_perimeter_extrusion_width etc.) produce the same numbers as if ran through the back-end, with the assumption of not overriding layer height by the variable layer height editing tool or layer height modifiers. --- src/PrusaSlicer.cpp | 2 +- src/libslic3r/Config.cpp | 6 ++ src/libslic3r/Config.hpp | 67 +++++++++++---- src/libslic3r/Flow.cpp | 95 ++++++++++++++++++++- src/libslic3r/Flow.hpp | 10 +++ src/libslic3r/PlaceholderParser.cpp | 72 ++++++++++------ src/libslic3r/Print.cpp | 19 +---- src/libslic3r/Print.hpp | 1 - src/libslic3r/PrintConfig.cpp | 3 +- src/libslic3r/PrintConfig.hpp | 27 +----- tests/libslic3r/test_placeholder_parser.cpp | 23 ++++- 11 files changed, 236 insertions(+), 89 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 28aadc045..7662288d3 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -591,7 +591,7 @@ bool CLI::setup(int argc, char **argv) // Initialize with defaults. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) for (const std::pair &optdef : *options) - m_config.optptr(optdef.first, true); + m_config.option(optdef.first, true); set_data_dir(m_config.opt_string("datadir")); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index bcd017278..6806f4f54 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -757,6 +757,12 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre return opt; } +const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) const +{ + auto it = options.find(opt_key); + return (it == options.end()) ? nullptr : it->second.get(); +} + void DynamicConfig::read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys) { std::vector args; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 9b04ae026..1258cc3f1 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1494,8 +1494,49 @@ protected: ConfigOptionDef* add_nullable(const t_config_option_key &opt_key, ConfigOptionType type); }; +// A pure interface to resolving ConfigOptions. +// This pure interface is useful as a base of ConfigBase, also it may be overriden to combine +// various config sources. +class ConfigOptionResolver +{ +public: + ConfigOptionResolver() {} + virtual ~ConfigOptionResolver() {} + + // Find a ConfigOption instance for a given name. + virtual const ConfigOption* optptr(const t_config_option_key &opt_key) const = 0; + + bool has(const t_config_option_key &opt_key) const { return this->optptr(opt_key) != nullptr; } + + const ConfigOption* option(const t_config_option_key &opt_key) const { return this->optptr(opt_key); } + + template + const TYPE* option(const t_config_option_key& opt_key) const + { + const ConfigOption* opt = this->optptr(opt_key); + return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast(opt); + } + + const ConfigOption* option_throw(const t_config_option_key& opt_key) const + { + const ConfigOption* opt = this->optptr(opt_key); + if (opt == nullptr) + throw UnknownOptionException(opt_key); + return opt; + } + + template + const TYPE* option_throw(const t_config_option_key& opt_key) const + { + const ConfigOption* opt = this->option_throw(opt_key); + if (opt->type() != TYPE::static_type()) + throw BadOptionTypeException("Conversion to a wrong type"); + return static_cast(opt); + } +}; + // An abstract configuration store. -class ConfigBase +class ConfigBase : public ConfigOptionResolver { public: // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. @@ -1503,7 +1544,7 @@ public: // but it carries the defaults of the configuration values. ConfigBase() {} - virtual ~ConfigBase() {} + ~ConfigBase() override {} // Virtual overridables: public: @@ -1513,6 +1554,7 @@ public: virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; // Collect names of all configuration values maintained by this configuration store. virtual t_config_option_keys keys() const = 0; + protected: // Verify whether the opt_key has not been obsoleted or renamed. // Both opt_key and value may be modified by handle_legacy(). @@ -1521,12 +1563,10 @@ protected: virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {} public: + using ConfigOptionResolver::option; + using ConfigOptionResolver::option_throw; + // Non-virtual methods: - bool has(const t_config_option_key &opt_key) const { return this->option(opt_key) != nullptr; } - - const ConfigOption* option(const t_config_option_key &opt_key) const - { return const_cast(this)->option(opt_key, false); } - ConfigOption* option(const t_config_option_key &opt_key, bool create = false) { return this->optptr(opt_key, create); } @@ -1537,10 +1577,6 @@ public: return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast(opt); } - template - const TYPE* option(const t_config_option_key &opt_key) const - { return const_cast(this)->option(opt_key, false); } - ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false) { ConfigOption *opt = this->optptr(opt_key, create); @@ -1549,9 +1585,6 @@ public: return opt; } - const ConfigOption* option_throw(const t_config_option_key &opt_key) const - { return const_cast(this)->option_throw(opt_key, false); } - template TYPE* option_throw(const t_config_option_key &opt_key, bool create = false) { @@ -1561,10 +1594,6 @@ public: return static_cast(opt); } - template - const TYPE* option_throw(const t_config_option_key &opt_key) const - { return const_cast(this)->option_throw(opt_key, false); } - // Apply all keys of other ConfigBase defined by this->def() to this ConfigBase. // An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(), // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. @@ -1735,6 +1764,8 @@ public: { return dynamic_cast(this->option(opt_key, create)); } template const T* opt(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key)); } + // Overrides ConfigResolver::optptr(). + const ConfigOption* optptr(const t_config_option_key &opt_key) const override; // Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override; // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 2c5c64fe7..9ac91c51d 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -1,12 +1,17 @@ #include "Flow.hpp" +#include "I18N.hpp" #include "Print.hpp" #include #include + +// Mark string for localization and translate. +#define L(s) Slic3r::I18N::translate(s) + namespace Slic3r { // This static method returns a sane extrusion width default. -static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, float height) +float Flow::auto_extrusion_width(FlowRole role, float nozzle_diameter) { switch (role) { case frSupportMaterial: @@ -22,6 +27,92 @@ static inline float auto_extrusion_width(FlowRole role, float nozzle_diameter, f } } +// Used by the Flow::extrusion_width() funtion to provide hints to the user on default extrusion width values, +// and to provide reasonable values to the PlaceholderParser. +static inline FlowRole opt_key_to_flow_role(const std::string &opt_key) +{ + if (opt_key == "perimeter_extrusion_width" || + // or all the defaults: + opt_key == "extrusion_width" || opt_key == "first_layer_extrusion_width") + return frPerimeter; + else if (opt_key == "external_perimeter_extrusion_width") + return frExternalPerimeter; + else if (opt_key == "infill_extrusion_width") + return frInfill; + else if (opt_key == "solid_infill_extrusion_width") + return frSolidInfill; + else if (opt_key == "top_infill_extrusion_width") + return frTopSolidInfill; + else if (opt_key == "support_material_extrusion_width") + return frSupportMaterial; + else + throw std::runtime_error("opt_key_to_flow_role: invalid argument"); +}; + +static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key) +{ + throw std::runtime_error((boost::format(L("Cannot calculate extrusion width for %1%: Variable \"%2%\" not accessible.")) % opt_key % dependent_opt_key).str()); +} + +// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser. +double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloatOrPercent* opt, const ConfigOptionResolver& config, const unsigned int first_printing_extruder) +{ + assert(opt != nullptr); + + bool first_layer = boost::starts_with(opt_key, "first_layer_"); + +#if 0 +// This is the logic used for skit / brim, but not for the rest of the 1st layer. + if (opt->value == 0. && first_layer) { + // The "first_layer_extrusion_width" was set to zero, try a substitute. + opt = config.option("perimeter_extrusion_width"); + if (opt == nullptr) + throw_on_missing_variable(opt_key, "perimeter_extrusion_width"); + } +#endif + + if (opt->value == 0.) { + // The role specific extrusion width value was set to zero, try the role non-specific extrusion width. + opt = config.option("extrusion_width"); + if (opt == nullptr) + throw_on_missing_variable(opt_key, "extrusion_width"); + // Use the "layer_height" instead of "first_layer_height". + first_layer = false; + } + + if (opt->percent) { + auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height"; + auto opt_layer_height = config.option(opt_key_layer_height); + if (opt_layer_height == nullptr) + throw_on_missing_variable(opt_key, opt_key_layer_height); + double layer_height = opt_layer_height->getFloat(); + if (first_layer && static_cast(opt_layer_height)->percent) { + // first_layer_height depends on layer_height. + opt_layer_height = config.option("layer_height"); + if (opt_layer_height == nullptr) + throw_on_missing_variable(opt_key, "layer_height"); + layer_height *= 0.01 * opt_layer_height->getFloat(); + } + return opt->get_abs_value(layer_height); + } + + if (opt->value == 0.) { + // If user left option to 0, calculate a sane default width. + auto opt_nozzle_diameters = config.option("nozzle_diameter"); + if (opt_nozzle_diameters == nullptr) + throw_on_missing_variable(opt_key, "nozzle_diameter"); + return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder))); + } + + return opt->value; +} + +// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser. +double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder) +{ + return extrusion_width(opt_key, config.option(opt_key), config, first_printing_extruder); +} + // This constructor builds a Flow object from an extrusion width config setting // and other context properties. Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) @@ -39,7 +130,7 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent sqrt(bridge_flow_ratio) * nozzle_diameter; } else if (! width.percent && width.value == 0.) { // If user left option to 0, calculate a sane default width. - w = auto_extrusion_width(role, nozzle_diameter, height); + w = auto_extrusion_width(role, nozzle_diameter); } else { // If user set a manual value, use it. w = float(width.get_abs_value(height)); diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 51cc4da9d..aad189775 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -64,6 +64,16 @@ public: // This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale // to fit a region with integer number of lines. static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + + // Sane extrusion width defautl based on nozzle diameter. + // The defaults were derived from manual Prusa MK3 profiles. + static float auto_extrusion_width(FlowRole role, float nozzle_diameter); + + // Extrusion width from full config, taking into account the defaults (when set to zero) and ratios (percentages). + // Precise value depends on layer index (1st layer vs. other layers vs. variable layer height), + // on active extruder etc. Therefore the value calculated by this function shall be used as a hint only. + static double extrusion_width(const std::string &opt_key, const ConfigOptionFloatOrPercent *opt, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); + static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0); }; extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index db3134ded..527d82b4c 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,4 +1,5 @@ #include "PlaceholderParser.hpp" +#include "Flow.hpp" #include #include #include @@ -99,11 +100,7 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf const ConfigOption *opt_old = config_old.option(opt_key); const ConfigOption *opt_new = config_new.option(opt_key); assert(opt_new != nullptr); - if (opt_old == nullptr) - return false; - return (opt_new->type() == coFloatOrPercent) ? - dynamic_cast(opt_old)->value == config_new.get_abs_value(opt_key) : - *opt_new == *opt_old; + return opt_old != nullptr && *opt_new == *opt_old; } std::vector PlaceholderParser::config_diff(const DynamicPrintConfig &rhs) @@ -126,14 +123,7 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) bool modified = false; for (const t_config_option_key &opt_key : rhs.keys()) { if (! opts_equal(m_config, rhs, opt_key)) { - // Store a copy of the config option. - // Convert FloatOrPercent values to floats first. - //FIXME there are some ratio_over chains, which end with empty ratio_with. - // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - const ConfigOption *opt_rhs = rhs.option(opt_key); - this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? - new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : - opt_rhs->clone()); + this->set(opt_key, rhs.option(opt_key)->clone()); modified = true; } } @@ -142,16 +132,8 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vector &keys) { - for (const t_config_option_key &opt_key : keys) { - // Store a copy of the config option. - // Convert FloatOrPercent values to floats first. - //FIXME there are some ratio_over chains, which end with empty ratio_with. - // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. - const ConfigOption *opt_rhs = rhs.option(opt_key); - this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? - new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : - opt_rhs->clone()); - } + for (const t_config_option_key &opt_key : keys) + this->set(opt_key, rhs.option(opt_key)->clone()); } void PlaceholderParser::apply_config(DynamicPrintConfig &&rhs) @@ -635,7 +617,7 @@ namespace client return os; } - struct MyContext { + struct MyContext : public ConfigOptionResolver { const DynamicConfig *external_config = nullptr; const DynamicConfig *config = nullptr; const DynamicConfig *config_override = nullptr; @@ -650,7 +632,7 @@ namespace client static void evaluate_full_macro(const MyContext *ctx, bool &result) { result = ! ctx->just_boolean_expression; } - const ConfigOption* resolve_symbol(const std::string &opt_key) const + const ConfigOption* optptr(const t_config_option_key &opt_key) const override { const ConfigOption *opt = nullptr; if (config_override != nullptr) @@ -662,6 +644,8 @@ namespace client return opt; } + const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } + template static void legacy_variable_expansion( const MyContext *ctx, @@ -758,7 +742,43 @@ namespace client case coPoint: output.set_s(opt.opt->serialize()); break; case coBool: output.set_b(opt.opt->getBool()); break; case coFloatOrPercent: - ctx->throw_exception("FloatOrPercent variables are not supported", opt.it_range); + { + std::string opt_key(opt.it_range.begin(), opt.it_range.end()); + if (boost::ends_with(opt_key, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); + } else if (! static_cast(opt.opt)->percent) { + // Not a percent, just return the value. + output.set_d(opt.opt->getFloat()); + } else { + // Resolve dependencies using the "ratio_over" link to a parent value. + const ConfigOptionDef *opt_def = print_config_def.get(opt_key); + assert(opt_def != nullptr); + double v = opt.opt->getFloat() * 0.01; // percent to ratio + for (;;) { + const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); + if (opt_parent == nullptr) + ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); + if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + assert(opt_parent->type() == coFloatOrPercent); + v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); + break; + } + if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { + v *= opt_parent->getFloat(); + if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) + break; + v *= 0.01; // percent to ratio + } + // Continue one level up in the "ratio_over" hierarchy. + opt_def = print_config_def.get(opt_def->ratio_over); + assert(opt_def != nullptr); + } + output.set_d(v); + } + break; + } default: ctx->throw_exception("Unknown scalar variable type", opt.it_range); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3131fd3d1..2e6d5f8af 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -24,8 +24,7 @@ #include #include -//! macro used to mark string used at localization, -//! return same string +// Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -527,7 +526,6 @@ void Print::config_diffs( const DynamicPrintConfig &new_full_config, t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff, t_config_option_keys &full_config_diff, - DynamicPrintConfig &placeholder_parser_overrides, DynamicPrintConfig &filament_overrides) const { // Collect changes to print config, account for overrides of extruder retract values by filament presets. @@ -563,19 +561,11 @@ void Print::config_diffs( object_diff = m_default_object_config.diff(new_full_config); region_diff = m_default_region_config.diff(new_full_config); // Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser. - // As the PlaceholderParser does not interpret the FloatOrPercent values itself, these values are stored into the PlaceholderParser converted to floats. for (const t_config_option_key &opt_key : new_full_config.keys()) { const ConfigOption *opt_old = m_full_print_config.option(opt_key); const ConfigOption *opt_new = new_full_config.option(opt_key); if (opt_old == nullptr || *opt_new != *opt_old) full_config_diff.emplace_back(opt_key); - if (opt_new->type() == coFloatOrPercent) { - // The m_placeholder_parser is never modified by the background processing, GCode.cpp/hpp makes a copy. - const ConfigOption *opt_old_pp = this->placeholder_parser().config().option(opt_key); - double new_value = new_full_config.get_abs_value(opt_key); - if (opt_old_pp == nullptr || static_cast(opt_old_pp)->value != new_value) - placeholder_parser_overrides.set_key_value(opt_key, new ConfigOptionFloat(new_value)); - } } } @@ -593,8 +583,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles. t_config_option_keys print_diff, object_diff, region_diff, full_config_diff; - DynamicPrintConfig placeholder_parser_overrides, filament_overrides; - this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, placeholder_parser_overrides, filament_overrides); + DynamicPrintConfig filament_overrides; + this->config_diffs(new_full_config, print_diff, object_diff, region_diff, full_config_diff, filament_overrides); // Do not use the ApplyStatus as we will use the max function when updating apply_status. unsigned int apply_status = APPLY_STATUS_UNCHANGED; @@ -614,9 +604,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // which should be stopped if print_diff is not empty. size_t num_extruders = m_config.nozzle_diameter.size(); bool num_extruders_changed = false; - if (! full_config_diff.empty() || ! placeholder_parser_overrides.empty()) { + if (! full_config_diff.empty()) { update_apply_status(this->invalidate_step(psGCodeExport)); - m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides)); // Set the profile aliases for the PrintBase::output_filename() m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone()); m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone()); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 75807cdda..1386b798e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -435,7 +435,6 @@ private: const DynamicPrintConfig &new_full_config, t_config_option_keys &print_diff, t_config_option_keys &object_diff, t_config_option_keys ®ion_diff, t_config_option_keys &full_config_diff, - DynamicPrintConfig &placeholder_parser_overrides, DynamicPrintConfig &filament_overrides) const; bool invalidate_state_by_config_options(const std::vector &opt_keys); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 47a72b829..ebb05772f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -718,8 +718,9 @@ void PrintConfigDef::init_fff_params() def->gui_type = "f_enum_open"; def->gui_flags = "show_value"; def->enum_values.push_back("PLA"); - def->enum_values.push_back("ABS"); def->enum_values.push_back("PET"); + def->enum_values.push_back("ABS"); + def->enum_values.push_back("ASA"); def->enum_values.push_back("FLEX"); def->enum_values.push_back("HIPS"); def->enum_values.push_back("EDGE"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8d14969c9..5130d3b05 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -46,12 +46,6 @@ enum SeamPosition { spRandom, spNearest, spAligned, spRear }; -/* -enum FilamentType { - ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA -}; -*/ - enum SLAMaterial { slamTough, slamFlex, @@ -149,24 +143,6 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::ge return keys_map; } -/* -template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { - static t_config_enum_values keys_map; - if (keys_map.empty()) { - keys_map["PLA"] = ftPLA; - keys_map["ABS"] = ftABS; - keys_map["PET"] = ftPET; - keys_map["HIPS"] = ftHIPS; - keys_map["FLEX"] = ftFLEX; - keys_map["SCAFF"] = ftSCAFF; - keys_map["EDGE"] = ftEDGE; - keys_map["NGEN"] = ftNGEN; - keys_map["PVA"] = ftPVA; - } - return keys_map; -} -*/ - template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static const t_config_enum_values keys_map = { { "landscape", sladoLandscape}, @@ -354,6 +330,9 @@ protected: #define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ + const ConfigOption* optptr(const t_config_option_key &opt_key) const override \ + { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ + /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 4d8217c16..e632dc705 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -14,7 +14,15 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { { "nozzle_diameter", "0.6;0.6;0.6;0.6" }, { "temperature", "357;359;363;378" } }); - parser.apply_config(config); + // To test the "first_layer_extrusion_width" over "first_layer_heigth" over "layer_height" chain. + config.option("first_layer_height")->value = 150.; + config.option("first_layer_height")->percent = true; + // To let the PlaceholderParser throw when referencing first_layer_speed if it is set to percent, as the PlaceholderParser does not know + // a percent to what. + config.option("first_layer_speed")->value = 50.; + config.option("first_layer_speed")->percent = true; + + parser.apply_config(config); parser.set("foo", 0); parser.set("bar", 2); parser.set("num_extruders", 4); @@ -41,6 +49,19 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("math: int(13.4)") { REQUIRE(parser.process("{int(13.4)}") == "13"); } SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); } + // Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions. + // first_layer_extrusion_width ratio_over first_layer_heigth ratio_over layer_height + SECTION("perimeter_extrusion_width") { REQUIRE(std::stod(parser.process("{perimeter_extrusion_width}")) == Approx(0.67500001192092896)); } + SECTION("first_layer_extrusion_width") { REQUIRE(std::stod(parser.process("{first_layer_extrusion_width}")) == Approx(0.9)); } + SECTION("support_material_xy_spacing") { REQUIRE(std::stod(parser.process("{support_material_xy_spacing}")) == Approx(0.3375)); } + // external_perimeter_speed over perimeter_speed + SECTION("external_perimeter_speed") { REQUIRE(std::stod(parser.process("{external_perimeter_speed}")) == Approx(30.)); } + // infill_overlap over perimeter_extrusion_width + SECTION("infill_overlap") { REQUIRE(std::stod(parser.process("{infill_overlap}")) == Approx(0.16875)); } + // If first_layer_speed is set to percent, then it is applied over respective extrusion types by overriding their respective speeds. + // The PlaceholderParser has no way to know which extrusion type the caller has in mind, therefore it throws. + SECTION("first_layer_speed") { REQUIRE_THROWS(parser.process("{first_layer_speed}")); } + // Test the boolean expression parser. auto boolean_expression = [&parser](const std::string& templ) { return parser.evaluate_boolean_expression(templ, parser.config()); }; From 450e21d4571decdb26637e7dd9ef45f368f658c7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Feb 2020 16:10:39 +0100 Subject: [PATCH 08/16] Replaced unsafe use of dx_url.substr(0, 54) with boost::starts_with --- src/slic3r/Utils/PresetUpdater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index ecdc12b50..3172a0682 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -301,9 +301,9 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors) const std::string idx_path = (cache_path / (vendor.id + ".idx")).string(); const std::string idx_path_temp = idx_path + "-update"; //check if idx_url is leading to our site - if(idx_url.substr(0, 54) != "http://files.prusa3d.com/wp-content/uploads/repository") + if (! boost::starts_with(idx_url, "http://files.prusa3d.com/wp-content/uploads/repository/")) { - BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor: " << vendor.name; + BOOST_LOG_TRIVIAL(warning) << "unsafe url path for vendor \"" << vendor.name << "\" rejected: " << idx_url; continue; } if (!get_file(idx_url, idx_path_temp)) { continue; } From de4c2fc2e0f6d501f660217586f9adf0b7fd4587 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 4 Feb 2020 17:44:06 +0100 Subject: [PATCH 09/16] DoubleSlider: SingleExtruder mode: Implemented editing of color from colored band, using RightMouseClick on it. And added corresponding tooltip --- src/slic3r/GUI/DoubleSlider.cpp | 70 +++++++++++++++++++++++++++------ src/slic3r/GUI/DoubleSlider.hpp | 6 ++- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 59c7de8ea..75f9b2068 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -603,21 +603,24 @@ std::string Control::get_color_for_color_change_tick(std::set::const_i return ""; } -void Control::draw_colored_band(wxDC& dc) +wxRect Control::get_colored_band_rect() { - if (!m_is_enabled_tick_manipulation) - return; - int height, width; get_size(&width, &height); const wxCoord mid = is_horizontal() ? 0.5 * height : 0.5 * width; - wxRect main_band = is_horizontal() ? - wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), - width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : - wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, - lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); + return is_horizontal() ? + wxRect(SLIDER_MARGIN, lround(mid - 0.375 * m_thumb_size.y), + width - 2 * SLIDER_MARGIN + 1, lround(0.75 * m_thumb_size.y)) : + wxRect(lround(mid - 0.375 * m_thumb_size.x), SLIDER_MARGIN, + lround(0.75 * m_thumb_size.x), height - 2 * SLIDER_MARGIN + 1); +} + +void Control::draw_colored_band(wxDC& dc) +{ + if (!m_is_enabled_tick_manipulation) + return; auto draw_band = [](wxDC& dc, const wxColour& clr, const wxRect& band_rc) { @@ -626,6 +629,8 @@ void Control::draw_colored_band(wxDC& dc) dc.DrawRectangle(band_rc); }; + wxRect main_band = get_colored_band_rect(); + // don't color a band for MultiExtruder mode if (m_ticks.empty() || m_mode == t_mode::MultiExtruder) { @@ -887,7 +892,7 @@ wxString Control::get_tooltip(FocusItem focused_item, int tick/*=-1*/) return _(L("Set extruder sequence for whole print")); if (focused_item == fiColorBand) return m_mode != t_mode::SingleExtruder ? "" : - _(L("For edit current color use Right(Double) mouse click on colored band")); + _(L("For edit current color use right mouse button click on colored band")); wxString tooltip; const auto tick_code_it = m_ticks.ticks.find(TickCode{tick}); @@ -961,6 +966,23 @@ wxString Control::get_tooltip(FocusItem focused_item, int tick/*=-1*/) } +int Control::get_edited_tick_for_position(const wxPoint pos, const std::string& gcode /*= ColorChangeCode*/) +{ + if (m_ticks.empty()) + return -1; + + int tick = get_value_from_position(pos); + auto it = std::lower_bound(m_ticks.ticks.begin(), m_ticks.ticks.end(), TickCode{ tick }); + + while (it != m_ticks.ticks.begin()) { + --it; + if (it->gcode == gcode) + return it->tick; + } + + return -1; +} + void Control::OnMotion(wxMouseEvent& event) { bool action = false; @@ -985,6 +1007,9 @@ void Control::OnMotion(wxMouseEvent& event) focused_item = fiRevertIcon; else if (is_point_in_rect(pos, m_rect_cog_icon)) focused_item = fiCogIcon; + else if (m_mode == t_mode::SingleExtruder && is_point_in_rect(pos, get_colored_band_rect()) && + get_edited_tick_for_position(pos) >= 0 ) + focused_item = fiColorBand; else { focused_item = fiTick; tick = get_tick_near_point(pos); @@ -1242,7 +1267,7 @@ void Control::OnRightDown(wxMouseEvent& event) const wxClientDC dc(this); wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) + if (m_is_enabled_tick_manipulation && is_point_in_rect(pos, m_rect_tick_action)) { const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; if (m_ticks.ticks.find(TickCode{ tick }) == m_ticks.ticks.end()) // if on this Z doesn't exist tick @@ -1254,6 +1279,13 @@ void Control::OnRightDown(wxMouseEvent& event) return; } + if (m_is_enabled_tick_manipulation && m_mode == t_mode::SingleExtruder && + is_point_in_rect(pos, get_colored_band_rect())) + { + m_force_color_edit = true; + return; + } + detect_selected_slider(event.GetLogicalPosition(dc)); if (!m_selection) return; @@ -1424,6 +1456,17 @@ void Control::OnRightUp(wxMouseEvent& event) m_show_edit_menu = false; } + else if (m_force_color_edit) + { + const wxClientDC dc(this); + wxPoint pos = event.GetLogicalPosition(dc); + + int edited_tick = get_edited_tick_for_position(pos); + if (edited_tick >= 0) + edit_tick(edited_tick); + + m_force_color_edit = false; + } Refresh(); Update(); @@ -1587,9 +1630,10 @@ void Control::delete_current_tick() post_ticks_changed_event(code); } -void Control::edit_tick() +void Control::edit_tick(int tick/* = -1*/) { - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; + if (tick < 0) + tick = m_selection == ssLower ? m_lower_value : m_higher_value; const std::set::iterator it = m_ticks.ticks.find(TickCode{ tick }); if (it == m_ticks.ticks.end() || diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index 00f64a6f6..86c025131 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -225,7 +225,7 @@ public: void add_current_tick(bool call_from_keyboard = false); // delete current tick, when press "-" void delete_current_tick(); - void edit_tick(); + void edit_tick(int tick = -1); void edit_extruder_sequence(); ExtrudersSequence m_extruders_sequence; @@ -263,14 +263,17 @@ private: wxString get_label(const SelectedSlider& selection) const; void get_lower_and_higher_position(int& lower_pos, int& higher_pos); int get_value_from_position(const wxCoord x, const wxCoord y); + int get_value_from_position(const wxPoint pos) { return get_value_from_position(pos.x, pos.y); } wxCoord get_position_from_value(const int value); wxSize get_size(); void get_size(int *w, int *h); double get_double_value(const SelectedSlider& selection); wxString get_tooltip(FocusItem focused_item, int tick = -1); + int get_edited_tick_for_position(wxPoint pos, const std::string& gcode = ColorChangeCode); std::string get_color_for_tool_change_tick(std::set::const_iterator it) const; std::string get_color_for_color_change_tick(std::set::const_iterator it) const; + wxRect get_colored_band_rect(); // Get active extruders for tick. // Means one current extruder for not existing tick OR @@ -316,6 +319,7 @@ private: bool m_force_mode_apply = true; bool m_force_add_tick = false; bool m_force_delete_tick = false; + bool m_force_color_edit = false; t_mode m_mode = t_mode::SingleExtruder; int m_only_extruder = -1; From bca3e864551368c8c09b1361dc8aaa9cdb03e09b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 4 Feb 2020 17:46:23 +0100 Subject: [PATCH 10/16] Fixed invalidation of FDM back end if a custom per layer extruder change is added or removed. --- src/libslic3r/Print.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2e6d5f8af..3f8fd9b40 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -494,7 +494,7 @@ static bool custom_per_printz_gcodes_tool_changes_differ(const std::vectorgcode != ToolChangeCode) { // Skip any CustomGCode items, which are not tool changes. ++ it_a; From 799a10d0ec08a659c1463d04d3adb46502d8cbac Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 4 Feb 2020 21:59:55 +0100 Subject: [PATCH 11/16] Added missed tooltip text + fixed conflict's controlling for ToolChange tick --- src/slic3r/GUI/DoubleSlider.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 75f9b2068..ff5a56545 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -915,10 +915,14 @@ wxString Control::get_tooltip(FocusItem focused_item, int tick/*=-1*/) // Show list of actions with new tick tooltip += ( m_mode == t_mode::MultiAsSingle ? _(L("For add change extruder use left mouse button click")) : + m_mode == t_mode::SingleExtruder ? + _(L("For add color change use left mouse button click " + "if you want to use colors from default color list, " + "or Shift + left mouse button click if you want to select a color")) : _(L("For add color change use left mouse button click")) ) + " " + _(L("OR pres \"+\" key")) + "\n" + ( is_osx ? - _(L("For add another code use Ctrl + Left mouse button click")) : + _(L("For add another code use Ctrl + left mouse button click")) : _(L("For add another code use right mouse button click")) ); } @@ -1955,9 +1959,11 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod if (it == ticks.begin()) return tick.extruder == std::max(only_extruder, 1) ? ctMeaninglessToolChange : ctNone; - --it; - if (it->gcode == ToolChangeCode && tick.extruder == it->extruder) - return ctMeaninglessToolChange; + while (it != ticks.begin()) { + --it; + if (it->gcode == ToolChangeCode) + return tick.extruder == it->extruder ? ctMeaninglessToolChange : ctNone; + } } return ctNone; From a66dcf58f7c11fb358fb7f6d140c586a5202b981 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Feb 2020 09:57:22 +0100 Subject: [PATCH 12/16] Fix for failing build without PCH --- src/libslic3r/Flow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index 9ac91c51d..b367be022 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -4,6 +4,7 @@ #include #include +#include // Mark string for localization and translate. #define L(s) Slic3r::I18N::translate(s) From 930432e6a3ec24dd740ad74c4b99e8f019d2f235 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 5 Feb 2020 11:38:45 +0100 Subject: [PATCH 13/16] updating preset bundle bug fix: if forced update - does not check snapshots --- src/slic3r/Utils/PresetUpdater.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 3172a0682..4e33e9d6b 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -538,13 +538,16 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version } // Check if the update is already present in a snapshot - const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version); - if (recommended_snap != SnapshotDB::singleton().end()) { - BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...") - % vp.name - % recommended->config_version.to_string() - % recommended_snap->id; - continue; + if(!current_not_supported) + { + const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version); + if (recommended_snap != SnapshotDB::singleton().end()) { + BOOST_LOG_TRIVIAL(info) << boost::format("Bundle update %1% %2% already found in snapshot %3%, skipping...") + % vp.name + % recommended->config_version.to_string() + % recommended_snap->id; + continue; + } } updates.updates.emplace_back(std::move(new_update)); From fce9f7747221641be57b1a676a8d6fe72dabf816 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 5 Feb 2020 13:14:50 +0100 Subject: [PATCH 14/16] Translations with arrow keys with modifiers: Shift -> slower translations Ctrl -> translations in camera space --- src/slic3r/GUI/GLCanvas3D.cpp | 155 ++++++++++++++++++++++++++++------ 1 file changed, 127 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 16fe7696c..2a4cf1849 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2702,8 +2702,127 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) } } +class TranslationProcessor +{ + using UpAction = std::function; + using DownAction = std::function; + + UpAction m_up_action{ nullptr }; + DownAction m_down_action{ nullptr }; + + bool m_running{ false }; + Vec3d m_direction{ Vec3d::UnitX() }; + +public: + TranslationProcessor(UpAction up_action, DownAction down_action) + : m_up_action(up_action), m_down_action(down_action) + { + } + + void process(wxKeyEvent& evt) + { + const int keyCode = evt.GetKeyCode(); + wxEventType type = evt.GetEventType(); + if (type == wxEVT_KEY_UP) { + switch (keyCode) + { + case WXK_NUMPAD_LEFT: case WXK_LEFT: + case WXK_NUMPAD_RIGHT: case WXK_RIGHT: + case WXK_NUMPAD_UP: case WXK_UP: + case WXK_NUMPAD_DOWN: case WXK_DOWN: + { + m_running = false; + m_up_action(); + break; + } + default: { break; } + } + } + else if (type == wxEVT_KEY_DOWN) { + bool apply = false; + + switch (keyCode) + { + case WXK_SHIFT: + { + if (m_running) + apply = true; + + break; + } + case WXK_NUMPAD_LEFT: + case WXK_LEFT: + { + m_direction = -Vec3d::UnitX(); + apply = true; + break; + } + case WXK_NUMPAD_RIGHT: + case WXK_RIGHT: + { + m_direction = Vec3d::UnitX(); + apply = true; + break; + } + case WXK_NUMPAD_UP: + case WXK_UP: + { + m_direction = Vec3d::UnitY(); + apply = true; + break; + } + case WXK_NUMPAD_DOWN: + case WXK_DOWN: + { + m_direction = -Vec3d::UnitY(); + apply = true; + break; + } + default: { break; } + } + + if (apply) { + m_running = true; + m_down_action(m_direction, evt.ShiftDown(), evt.CmdDown()); + } + } + } +}; + void GLCanvas3D::on_key(wxKeyEvent& evt) { + static TranslationProcessor translationProcessor( + [this]() { + do_move(L("Gizmo-Move")); + m_gizmos.update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + // Let the plater know that the dragging finished, so a delayed refresh + // of the scene with the background processing data should be performed. + post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + refresh_camera_scene_box(); + m_dirty = true; + }, + [this](const Vec3d& direction, bool slow, bool camera_space) { + m_selection.start_dragging(); + double multiplier = slow ? 1.0 : 10.0; + + Vec3d displacement; + if (camera_space) + { + Eigen::Matrix inv_view_3x3 = m_camera.get_view_matrix().inverse().matrix().block(0, 0, 3, 3); + displacement = multiplier * (inv_view_3x3 * direction); + displacement(2) = 0.0; + } + else + displacement = multiplier * direction; + + m_selection.translate(displacement); + m_dirty = true; + } + ); + const int keyCode = evt.GetKeyCode(); auto imgui = wxGetApp().imgui(); @@ -2722,6 +2841,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_SHIFT) { + translationProcessor.process(evt); + if (m_picking_enabled && m_rectangle_selection.is_dragging()) { _update_selection_from_hover(); @@ -2745,26 +2866,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) else if (keyCode == WXK_CONTROL) m_dirty = true; else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { + translationProcessor.process(evt); + switch (keyCode) { - case WXK_NUMPAD_LEFT: case WXK_LEFT: - case WXK_NUMPAD_RIGHT: case WXK_RIGHT: - case WXK_NUMPAD_UP: case WXK_UP: - case WXK_NUMPAD_DOWN: case WXK_DOWN: - { - do_move(L("Gizmo-Move")); - m_gizmos.update_data(); - - wxGetApp().obj_manipul()->set_dirty(); - // Let the plater know that the dragging finished, so a delayed refresh - // of the scene with the background processing data should be performed. - post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); - // updates camera target constraints - refresh_camera_scene_box(); - m_dirty = true; - - break; - } case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { @@ -2789,6 +2894,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers(); if (keyCode == WXK_SHIFT) { + translationProcessor.process(evt); + if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) { m_mouse.ignore_left_up = false; @@ -2807,12 +2914,6 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) m_dirty = true; else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { - auto do_move = [this](const Vec3d& displacement) { - m_selection.start_dragging(); - m_selection.translate(displacement); - m_dirty = true; -// wxGetApp().obj_manipul()->set_dirty(); - }; auto do_rotate = [this](double angle_z_rad) { m_selection.start_dragging(); m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); @@ -2820,12 +2921,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) // wxGetApp().obj_manipul()->set_dirty(); }; + translationProcessor.process(evt); + switch (keyCode) { - case WXK_NUMPAD_LEFT: case WXK_LEFT: { do_move(-Vec3d::UnitX()); break; } - case WXK_NUMPAD_RIGHT: case WXK_RIGHT: { do_move(Vec3d::UnitX()); break; } - case WXK_NUMPAD_UP: case WXK_UP: { do_move(Vec3d::UnitY()); break; } - case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-Vec3d::UnitY()); break; } case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: { do_rotate(0.25 * M_PI); break; } case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { do_rotate(-0.25 * M_PI); break; } default: { break; } From 5c972ce9ab387dc9b644d5b0c01d6317d80ab1bc Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 5 Feb 2020 13:57:15 +0100 Subject: [PATCH 15/16] config wizard bugfix: changed order of creating pages --- src/slic3r/GUI/ConfigWizard.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 880d1127b..9edc3ac39 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2001,6 +2001,10 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->page_msla = new PagePrinters(this, _(L("Prusa MSLA Technology Printers")), "Prusa MSLA", *vendor_prusa, 0, T_SLA); p->add_page(p->page_msla); + // Pages for 3rd party vendors + p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors + p->add_page(p->page_vendors = new PageVendors(this)); + p->any_sla_selected = p->check_sla_selected(); p->any_fff_selected = p->check_fff_selected(); @@ -2022,10 +2026,6 @@ ConfigWizard::ConfigWizard(wxWindow *parent) p->add_page(p->page_diams = new PageDiameters(this)); p->add_page(p->page_temps = new PageTemperatures(this)); - // Pages for 3rd party vendors - p->create_3rdparty_pages(); // Needs to be done _before_ creating PageVendors - p->add_page(p->page_vendors = new PageVendors(this)); - p->load_pages(); p->index->go_to(size_t{0}); From b0588abdd2e47d1e47a9715d3c2e3319cf7490db Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Feb 2020 14:21:06 +0100 Subject: [PATCH 16/16] Invalidate background process when HollowingGizmo modifies settings Also, assertion fail in debug mode for m_selection --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index e3b18792e..11a78fd60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -352,8 +352,9 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairhas_drilled_mesh()) { m_c->m_model_object->sla_drain_holes.push_back(sla::DrainHole()); + m_selected.push_back(false); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); - wxGetApp().CallAfter([this] { m_c->m_model_object->sla_drain_holes.pop_back();}); + wxGetApp().CallAfter([this] { m_c->m_model_object->sla_drain_holes.pop_back(); m_selected.pop_back(); }); return false; } @@ -667,6 +668,7 @@ void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) bool first_run = true; // This is a hack to redraw the button when all points are removed, // so it is not delayed until the background process finishes. + RENDER_AGAIN: const float approx_height = m_imgui->scaled(20.0f); y = std::min(y, bottom_limit - approx_height); @@ -692,6 +694,8 @@ RENDER_AGAIN: if (m_imgui->button(m_desc["preview"])) hollow_mesh(); + + bool config_changed = false; ImGui::Separator(); @@ -701,6 +705,7 @@ RENDER_AGAIN: if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { m_c->m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; wxGetApp().obj_list()->update_and_show_object_settings_item(); + config_changed = true; } } @@ -776,8 +781,10 @@ RENDER_AGAIN: m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = offset; m_c->m_model_object->config.opt("hollowing_quality", true)->value = quality; m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = closing_d; - if (slider_released) + if (slider_released) { wxGetApp().obj_list()->update_and_show_object_settings_item(); + config_changed = true; + } } m_imgui->disabled_end(); @@ -902,6 +909,9 @@ RENDER_AGAIN: if (force_refresh) m_parent.set_as_dirty(); + + if (config_changed) + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); } bool GLGizmoHollow::on_is_activable() const