diff --git a/resources/icons/overlay/cut_hover.png b/resources/icons/overlay/cut_hover.png index 99dc4cf8d..a9758cfd9 100644 Binary files a/resources/icons/overlay/cut_hover.png and b/resources/icons/overlay/cut_hover.png differ diff --git a/resources/icons/overlay/cut_off.png b/resources/icons/overlay/cut_off.png index cd4f130e1..f64179cf8 100644 Binary files a/resources/icons/overlay/cut_off.png and b/resources/icons/overlay/cut_off.png differ diff --git a/resources/icons/overlay/cut_on.png b/resources/icons/overlay/cut_on.png index 7e78e0e6a..8a4c3515e 100644 Binary files a/resources/icons/overlay/cut_on.png and b/resources/icons/overlay/cut_on.png differ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index c10e90bdb..871ac9497 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -981,19 +981,25 @@ template static void cut_reset_transform(T *thing) { thing->set_offset(offset); } -ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z) +ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower) { // Clone the object to duplicate instances, materials etc. - ModelObject* upper = ModelObject::new_clone(*this); - ModelObject* lower = ModelObject::new_clone(*this); - upper->set_model(nullptr); - lower->set_model(nullptr); - upper->sla_support_points.clear(); - lower->sla_support_points.clear(); - upper->clear_volumes(); - lower->clear_volumes(); - upper->input_file = ""; - lower->input_file = ""; + ModelObject* upper = keep_upper ? ModelObject::new_clone(*this) : nullptr; + ModelObject* lower = keep_lower ? ModelObject::new_clone(*this) : nullptr; + + if (keep_upper) { + upper->set_model(nullptr); + upper->sla_support_points.clear(); + upper->clear_volumes(); + upper->input_file = ""; + } + + if (keep_lower) { + lower->set_model(nullptr); + lower->sla_support_points.clear(); + lower->clear_volumes(); + lower->input_file = ""; + } const auto instance_matrix = instances[instance]->get_matrix(true); @@ -1008,14 +1014,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z) const auto bb = instance_bounding_box(instance, true); z -= bb.min(2); - for (auto *instance : upper->instances) { cut_reset_transform(instance); } - for (auto *instance : lower->instances) { cut_reset_transform(instance); } + if (keep_upper) { + for (auto *instance : upper->instances) { cut_reset_transform(instance); } + } + if (keep_lower) { + for (auto *instance : lower->instances) { cut_reset_transform(instance); } + } for (ModelVolume *volume : volumes) { if (! volume->is_model_part()) { // don't cut modifiers - upper->add_volume(*volume); - lower->add_volume(*volume); + if (keep_upper) { upper->add_volume(*volume); } + if (keep_lower) { lower->add_volume(*volume); } } else { TriangleMesh upper_mesh, lower_mesh; @@ -1026,18 +1036,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z) TriangleMeshSlicer tms(&volume->mesh); tms.cut(z, &upper_mesh, &lower_mesh); - upper_mesh.repair(); - lower_mesh.repair(); - upper_mesh.reset_repair_stats(); - lower_mesh.reset_repair_stats(); + if (keep_upper) { + upper_mesh.repair(); + upper_mesh.reset_repair_stats(); + } + if (keep_lower) { + lower_mesh.repair(); + lower_mesh.reset_repair_stats(); + } - if (upper_mesh.facets_count() > 0) { + if (keep_upper && upper_mesh.facets_count() > 0) { ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; vol->config = volume->config; vol->set_material(volume->material_id(), *volume->material()); } - if (lower_mesh.facets_count() > 0) { + if (keep_lower && lower_mesh.facets_count() > 0) { ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; vol->config = volume->config; @@ -1046,12 +1060,25 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z) } } - upper->invalidate_bounding_box(); - lower->invalidate_bounding_box(); + if (keep_lower && rotate_lower) { + for (auto *instance : lower->instances) { + Geometry::Transformation tr; + tr.set_offset(instance->get_offset()); + tr.set_rotation({Geometry::deg2rad(180.0), 0.0, 0.0}); + instance->set_transformation(tr); + } + } ModelObjectPtrs res; - if (upper->volumes.size() > 0) { res.push_back(upper); } - if (lower->volumes.size() > 0) { res.push_back(lower); } + + if (keep_upper && upper->volumes.size() > 0) { + upper->invalidate_bounding_box(); + res.push_back(upper); + } + if (keep_lower && lower->volumes.size() > 0) { + lower->invalidate_bounding_box(); + res.push_back(lower); + } return res; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c288010d0..8b8a0f59b 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -238,7 +238,7 @@ public: size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; - ModelObjectPtrs cut(size_t instance, coordf_t z); + ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); void split(ModelObjectPtrs* new_objects); void repair(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ee3951f02..d54ec650c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2893,7 +2893,7 @@ void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const GLCanvas3D: curr->render_for_picking(selection); } -void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas) const +void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas, const GLCanvas3D::Selection& selection) const { if (!m_enabled) return; @@ -2903,17 +2903,19 @@ void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas) const ::glPushMatrix(); ::glLoadIdentity(); - _render_overlay(canvas); + _render_overlay(canvas, selection); ::glPopMatrix(); } +#ifndef ENABLE_IMGUI void GLCanvas3D::Gizmos::create_external_gizmo_widgets(wxWindow *parent) { for (auto &entry : m_gizmos) { entry.second->create_external_gizmo_widgets(parent); } } +#endif // not ENABLE_IMGUI void GLCanvas3D::Gizmos::_reset() { @@ -2926,7 +2928,7 @@ void GLCanvas3D::Gizmos::_reset() m_gizmos.clear(); } -void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const +void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanvas3D::Selection& selection) const { if (m_gizmos.empty()) return; @@ -3326,7 +3328,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_moving(false) , m_color_by("volume") , m_reload_delayed(false) +#ifndef ENABLE_IMGUI , m_external_gizmo_widgets_parent(nullptr) +#endif // not ENABLE_IMGUI { if (m_canvas != nullptr) { @@ -3434,10 +3438,12 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) return false; } +#ifndef ENABLE_IMGUI if (m_external_gizmo_widgets_parent != nullptr) { m_gizmos.create_external_gizmo_widgets(m_external_gizmo_widgets_parent); m_canvas->GetParent()->Layout(); } +#endif // not ENABLE_IMGUI } if (!_init_toolbar()) @@ -3753,7 +3759,7 @@ void GLCanvas3D::render() set_tooltip(""); #if ENABLE_IMGUI - wxGetApp().get_imgui().new_frame(); + wxGetApp().imgui()->new_frame(); #endif // ENABLE_IMGUI // picking pass @@ -3799,7 +3805,7 @@ void GLCanvas3D::render() _render_layer_editing_overlay(); #if ENABLE_IMGUI - wxGetApp().get_imgui().render(); + wxGetApp().imgui()->render(); #endif // ENABLE_IMGUI m_canvas->SwapBuffers(); @@ -4462,7 +4468,13 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_mouse(wxMouseEvent& evt) { #if ENABLE_IMGUI - wxGetApp().get_imgui().update_mouse_data(evt); + auto imgui = wxGetApp().imgui(); + if (imgui->update_mouse_data(evt)) { + render(); + if (imgui->want_any_input()) { + return; + } + } #endif // ENABLE_IMGUI Point pos(evt.GetX(), evt.GetY()); @@ -4957,10 +4969,12 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const } } +#ifndef ENABLE_IMGUI void GLCanvas3D::set_external_gizmo_widgets_parent(wxWindow *parent) { m_external_gizmo_widgets_parent = parent; } +#endif // not ENABLE_IMGUI void GLCanvas3D::do_move() { @@ -5323,7 +5337,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) return; #if ENABLE_IMGUI - wxGetApp().get_imgui().set_display_size((float)w, (float)h); + wxGetApp().imgui()->set_display_size((float)w, (float)h); #endif // ENABLE_IMGUI // ensures that this canvas is current @@ -5796,7 +5810,7 @@ void GLCanvas3D::_render_current_gizmo() const void GLCanvas3D::_render_gizmos_overlay() const { - m_gizmos.render_overlay(*this); + m_gizmos.render_overlay(*this, m_selection); } void GLCanvas3D::_render_toolbar() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d98a0c8f0..3a6759393 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -636,14 +636,16 @@ private: void render_current_gizmo(const Selection& selection) const; void render_current_gizmo_for_picking_pass(const Selection& selection) const; - void render_overlay(const GLCanvas3D& canvas) const; + void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; +#ifndef ENABLE_IMGUI void create_external_gizmo_widgets(wxWindow *parent); +#endif // not ENABLE_IMGUI private: void _reset(); - void _render_overlay(const GLCanvas3D& canvas) const; + void _render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; void _render_current_gizmo(const Selection& selection) const; float _get_total_overlay_height() const; @@ -732,7 +734,9 @@ private: GCodePreviewVolumeIndex m_gcode_preview_volume_index; +#ifndef ENABLE_IMGUI wxWindow *m_external_gizmo_widgets_parent; +#endif // not ENABLE_IMGUI void post_event(wxEvent &&event); void viewport_changed(); @@ -852,7 +856,9 @@ public: void set_tooltip(const std::string& tooltip) const; +#ifndef ENABLE_IMGUI void set_external_gizmo_widgets_parent(wxWindow *parent); +#endif // not ENABLE_IMGUI void do_move(); void do_rotate(); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 03acdcbdd..ed2a6f2d2 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -170,6 +170,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent) #endif // ENABLE_GIZMOS_SHORTCUT , m_hover_id(-1) , m_dragging(false) + , m_imgui(wxGetApp().imgui()) { ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float)); ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float)); @@ -273,7 +274,9 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const } } +#ifndef ENABLE_IMGUI void GLGizmoBase::create_external_gizmo_widgets(wxWindow *parent) {} +#endif // not ENABLE_IMGUI void GLGizmoBase::set_tooltip(const std::string& tooltip) const { @@ -687,16 +690,16 @@ void GLGizmoRotate3D::on_render(const GLCanvas3D::Selection& selection) const } #if ENABLE_IMGUI -void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const +void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle())); - std::string label = _("Rotation (deg)"); + wxString label = _(L("Rotation (deg)")); - wxGetApp().get_imgui().set_next_window_pos(x, y, ImGuiCond_Always); - wxGetApp().get_imgui().set_next_window_bg_alpha(0.5f); - wxGetApp().get_imgui().begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - wxGetApp().get_imgui().input_vec3("", rotation, 100.0f, "%.2f"); - wxGetApp().get_imgui().end(); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + m_imgui->set_next_window_bg_alpha(0.5f); + m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->input_vec3("", rotation, 100.0f, "%.2f"); + m_imgui->end(); } #endif // ENABLE_IMGUI @@ -987,17 +990,16 @@ void GLGizmoScale3D::on_render_for_picking(const GLCanvas3D::Selection& selectio } #if ENABLE_IMGUI -void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const +void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { bool single_instance = selection.is_single_full_instance(); - Vec3d scale = single_instance ? 100.0 * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor() : 100.0 * m_scale; - std::string label = _("Scale (%)"); + wxString label = _(L("Scale (%)")); - wxGetApp().get_imgui().set_next_window_pos(x, y, ImGuiCond_Always); - wxGetApp().get_imgui().set_next_window_bg_alpha(0.5f); - wxGetApp().get_imgui().begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - wxGetApp().get_imgui().input_vec3("", scale, 100.0f, "%.2f"); - wxGetApp().get_imgui().end(); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + m_imgui->set_next_window_bg_alpha(0.5f); + m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->input_vec3("", m_scale, 100.0f, "%.2f"); + m_imgui->end(); } #endif // ENABLE_IMGUI @@ -1215,26 +1217,20 @@ void GLGizmoMove3D::on_render_for_picking(const GLCanvas3D::Selection& selection } #if ENABLE_IMGUI -void GLGizmoMove3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const +void GLGizmoMove3D::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); Vec3d displacement = show_position ? position : m_displacement; - std::string label = show_position ? _("Position (mm)") : _("Displacement (mm)"); + wxString label = show_position ? _(L("Position (mm)")) : _(L("Displacement (mm)")); - wxGetApp().get_imgui().set_next_window_pos(x, y, ImGuiCond_Always); - wxGetApp().get_imgui().set_next_window_bg_alpha(0.5f); - wxGetApp().get_imgui().begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - wxGetApp().get_imgui().input_vec3("", displacement, 100.0f, "%.2f"); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + m_imgui->set_next_window_bg_alpha(0.5f); + m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->input_vec3("", displacement, 100.0f, "%.2f"); - if (ImGui::Button("Test")) - { - std::cout << "WOW" << std::endl; - } - - - wxGetApp().get_imgui().end(); + m_imgui->end(); } #endif // ENABLE_IMGUI @@ -1932,9 +1928,15 @@ const std::array GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 }; GLGizmoCut::GLGizmoCut(GLCanvas3D& parent) : GLGizmoBase(parent) , m_cut_z(0.0) +#ifndef ENABLE_IMGUI , m_panel(nullptr) +#endif // not ENABLE_IMGUI + , m_keep_upper(true) + , m_keep_lower(true) + , m_rotate_lower(false) {} +#ifndef ENABLE_IMGUI void GLGizmoCut::create_external_gizmo_widgets(wxWindow *parent) { wxASSERT(m_panel == nullptr); @@ -1949,9 +1951,10 @@ void GLGizmoCut::create_external_gizmo_widgets(wxWindow *parent) m_panel->Hide(); m_panel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - perform_cut(); + perform_cut(m_parent.get_selection()); }, wxID_OK); } +#endif // not ENABLE_IMGUI bool GLGizmoCut::on_init() { @@ -1992,10 +1995,12 @@ void GLGizmoCut::on_set_state() m_cut_z = 0.0; } +#ifndef ENABLE_IMGUI // Display or hide the extra panel if (m_panel != nullptr) { m_panel->display(get_state() == On); } +#endif // not ENABLE_IMGUI } bool GLGizmoCut::on_is_activable(const GLCanvas3D::Selection& selection) const @@ -2080,16 +2085,38 @@ void GLGizmoCut::on_render_for_picking(const GLCanvas3D::Selection& selection) c render_grabbers_for_picking(selection.get_bounding_box()); } -void GLGizmoCut::perform_cut() +#if ENABLE_IMGUI +void GLGizmoCut::on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { - const auto &selection = m_parent.get_selection(); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + m_imgui->set_next_window_bg_alpha(0.5f); + m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + ImGui::PushItemWidth(100.0f); + bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f"); + + m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper); + m_imgui->checkbox(_(L("Keep lower part")), m_keep_lower); + m_imgui->checkbox(_(L("Rotate lower part upwards")), m_rotate_lower); + + const bool cut_clicked = m_imgui->button(_(L("Perform cut"))); + + m_imgui->end(); + + if (cut_clicked) { + perform_cut(selection); + } +} +#endif // ENABLE_IMGUI + +void GLGizmoCut::perform_cut(const GLCanvas3D::Selection& selection) +{ const auto instance_idx = selection.get_instance_idx(); const auto object_idx = selection.get_object_idx(); wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); - wxGetApp().plater()->cut(object_idx, instance_idx, m_cut_z); + wxGetApp().plater()->cut(object_idx, instance_idx, m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 146c04771..64bd71876 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -23,6 +23,7 @@ class ModelObject; namespace GUI { class GLCanvas3D; +class ImGuiWrapper; class GLGizmoBase { @@ -88,6 +89,7 @@ protected: float m_drag_color[3]; float m_highlight_color[3]; mutable std::vector m_grabbers; + ImGuiWrapper* m_imgui; public: explicit GLGizmoBase(GLCanvas3D& parent); @@ -135,10 +137,12 @@ public: void render(const GLCanvas3D::Selection& selection) const { on_render(selection); } void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); } +#ifndef ENABLE_IMGUI virtual void create_external_gizmo_widgets(wxWindow *parent); +#endif // not ENABLE_IMGUI #if ENABLE_IMGUI - void render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const { on_render_input_window(x, y, selection); } + void render_input_window(float x, float y, const GLCanvas3D::Selection& selection) { on_render_input_window(x, y, selection); } #endif // ENABLE_IMGUI protected: @@ -160,7 +164,7 @@ protected: virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const = 0; #if ENABLE_IMGUI - virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const {} + virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) {} #endif // ENABLE_IMGUI float picking_color_component(unsigned int id) const; @@ -301,7 +305,7 @@ protected: } #if ENABLE_IMGUI - virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const; + virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection); #endif // ENABLE_IMGUI }; @@ -341,7 +345,7 @@ protected: virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; #if ENABLE_IMGUI - virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const; + virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection); #endif // ENABLE_IMGUI private: @@ -385,7 +389,7 @@ protected: virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; #if ENABLE_IMGUI - virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) const; + virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection); #endif // ENABLE_IMGUI private: @@ -494,7 +498,9 @@ protected: }; +#ifndef ENABLE_IMGUI class GLGizmoCutPanel; +#endif // not ENABLE_IMGUI class GLGizmoCut : public GLGizmoBase { @@ -507,12 +513,21 @@ class GLGizmoCut : public GLGizmoBase double m_max_z; Vec3d m_drag_pos; Vec3d m_drag_center; + bool m_keep_upper; + bool m_keep_lower; + bool m_rotate_lower; +#ifndef ENABLE_IMGUI GLGizmoCutPanel *m_panel; +#endif // not ENABLE_IMGUI public: explicit GLGizmoCut(GLCanvas3D& parent); +#ifndef ENABLE_IMGUI virtual void create_external_gizmo_widgets(wxWindow *parent); +#endif // not ENABLE_IMGUI +#ifndef ENABLE_IMGUI +#endif // not ENABLE_IMGUI protected: virtual bool on_init(); @@ -524,8 +539,11 @@ protected: virtual void on_render(const GLCanvas3D::Selection& selection) const; virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const; +#if ENABLE_IMGUI + virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection); +#endif // ENABLE_IMGUI private: - void perform_cut(); + void perform_cut(const GLCanvas3D::Selection& selection); double calc_projection(const Linef3& mouse_ray) const; }; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 923d19443..0299e27e5 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -32,9 +32,6 @@ #include #include #include "SysInfoDialog.hpp" -#if ENABLE_IMGUI -#include -#endif // ENABLE_IMGUI namespace Slic3r { namespace GUI { @@ -58,11 +55,16 @@ const wxString file_wildcards[FT_SIZE] = { static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } IMPLEMENT_APP(GUI_App) + +GUI_App::GUI_App() + : wxApp() + , m_imgui(new ImGuiWrapper()) +{} + bool GUI_App::OnInit() { #if ENABLE_IMGUI - if (!m_imgui.init()) - return false; + wxCHECK_MSG(m_imgui->init(), false, "Failed to initialize ImGui"); #endif // ENABLE_IMGUI SetAppName("Slic3rPE-alpha"); @@ -185,14 +187,6 @@ bool GUI_App::OnInit() return true; } -#if ENABLE_IMGUI -int GUI_App::OnExit() -{ - m_imgui.shutdown(); - return 0; -} -#endif // ENABLE_IMGUI - unsigned GUI_App::get_colour_approx_luma(const wxColour &colour) { double r = colour.Red(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 451623e42..27c300257 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_GUI_App_hpp_ #define slic3r_GUI_App_hpp_ +#include #include #include "PrintConfig.hpp" #include "MainFrame.hpp" @@ -86,15 +87,13 @@ class GUI_App : public wxApp wxLocale* m_wxLocale{ nullptr }; #if ENABLE_IMGUI - ImGuiWrapper m_imgui; + std::unique_ptr m_imgui; #endif // ENABLE_IMGUI public: bool OnInit() override; -#if ENABLE_IMGUI - int OnExit() override; -#endif // ENABLE_IMGUI - GUI_App() : wxApp() {} + + GUI_App(); unsigned get_colour_approx_luma(const wxColour &colour); void init_label_colours(); @@ -162,7 +161,7 @@ public: std::vector tabs_list; #if ENABLE_IMGUI - ImGuiWrapper& get_imgui() { return m_imgui; } + ImGuiWrapper* imgui() { return m_imgui.get(); } #endif // ENABLE_IMGUI }; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 98baf1b36..aa8b58406 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1,13 +1,19 @@ -#include "../../libslic3r/libslic3r.h" #include "ImGuiWrapper.hpp" -#include "Utils.hpp" +#include +#include +#include + +#include #include +#include #include -#include +#include "libslic3r/libslic3r.h" +#include "GUI.hpp" +#include "Utils.hpp" namespace Slic3r { namespace GUI { @@ -26,9 +32,16 @@ ImGuiWrapper::ImGuiWrapper() , m_attrib_location_position(0) , m_attrib_location_uv(0) , m_attrib_location_color(0) + , m_mouse_buttons(0) { } +ImGuiWrapper::~ImGuiWrapper() +{ + destroy_device_objects(); + ImGui::DestroyContext(); +} + bool ImGuiWrapper::init() { // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. @@ -45,27 +58,21 @@ bool ImGuiWrapper::init() ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); - ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "\\fonts\\NotoSans-Regular.ttf").c_str(), 18.0f); - if (font == nullptr) - { + ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), 18.0f); + if (font == nullptr) { font = io.Fonts->AddFontDefault(); if (font == nullptr) return false; } - else + else { m_fonts.insert(FontsMap::value_type("Noto Sans Regular 18", font)); + } io.IniFilename = nullptr; return true; } -void ImGuiWrapper::shutdown() -{ - destroy_device_objects(); - ImGui::DestroyContext(); -} - void ImGuiWrapper::set_display_size(float w, float h) { ImGuiIO& io = ImGui::GetIO(); @@ -73,7 +80,7 @@ void ImGuiWrapper::set_display_size(float w, float h) io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); } -void ImGuiWrapper::update_mouse_data(wxMouseEvent& evt) +bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt) { ImGuiIO& io = ImGui::GetIO(); io.MousePos = ImVec2((float)evt.GetX(), (float)evt.GetY()); @@ -81,10 +88,10 @@ void ImGuiWrapper::update_mouse_data(wxMouseEvent& evt) io.MouseDown[1] = evt.RightDown(); io.MouseDown[2] = evt.MiddleDown(); - if (io.MouseDown[0]) - { - int a = 0; - } + unsigned buttons = evt.LeftDown() | evt.RightDown() << 1 | evt.MiddleDown() << 2; + bool res = buttons != m_mouse_buttons; + m_mouse_buttons = buttons; + return res; } void ImGuiWrapper::new_frame() @@ -97,9 +104,6 @@ void ImGuiWrapper::new_frame() void ImGuiWrapper::render() { - ImGuiIO& io = ImGui::GetIO(); - - ImGui::Render(); render_draw_data(ImGui::GetDrawData()); } @@ -114,22 +118,33 @@ void ImGuiWrapper::set_next_window_bg_alpha(float alpha) ImGui::SetNextWindowBgAlpha(alpha); } -bool ImGuiWrapper::begin(const std::string& name, int flags) +bool ImGuiWrapper::begin(const std::string &name, int flags) { return ImGui::Begin(name.c_str(), nullptr, (ImGuiWindowFlags)flags); } +bool ImGuiWrapper::begin(const wxString &name, int flags) +{ + return begin(into_u8(name), flags); +} + void ImGuiWrapper::end() { ImGui::End(); } -bool ImGuiWrapper::input_double(const std::string& label, double& value, const std::string& format) +bool ImGuiWrapper::button(const wxString &label) { - return ImGui::InputDouble(label.c_str(), &value, 0.0f, 0.0f, format.c_str()); + auto label_utf8 = into_u8(label); + return ImGui::Button(label_utf8.c_str()); } -bool ImGuiWrapper::input_vec3(const std::string& label, Vec3d& value, float width, const std::string& format) +bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) +{ + return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str()); +} + +bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format) { bool value_changed = false; @@ -140,7 +155,7 @@ bool ImGuiWrapper::input_vec3(const std::string& label, Vec3d& value, float widt std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z"); ImGui::PushID(i); ImGui::PushItemWidth(width); - value_changed |= ImGui::InputDouble(item_label.c_str(), &value(i), 0.0f, 0.0f, format.c_str()); + value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast(&value(i)), 0.0f, 0.0f, format.c_str()); ImGui::PopID(); } ImGui::EndGroup(); @@ -148,6 +163,33 @@ bool ImGuiWrapper::input_vec3(const std::string& label, Vec3d& value, float widt return value_changed; } +bool ImGuiWrapper::checkbox(const wxString &label, bool &value) +{ + auto label_utf8 = into_u8(label); + return ImGui::Checkbox(label_utf8.c_str(), &value); +} + +bool ImGuiWrapper::want_mouse() const +{ + return ImGui::GetIO().WantCaptureMouse; +} + +bool ImGuiWrapper::want_keyboard() const +{ + return ImGui::GetIO().WantCaptureKeyboard; +} + +bool ImGuiWrapper::want_text_input() const +{ + return ImGui::GetIO().WantTextInput; +} + +bool ImGuiWrapper::want_any_input() const +{ + const auto io = ImGui::GetIO(); + return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput; +} + void ImGuiWrapper::create_device_objects() { // Backup GL state @@ -289,19 +331,19 @@ void ImGuiWrapper::create_device_objects() m_vert_handle = glCreateShader(GL_VERTEX_SHADER); glShaderSource(m_vert_handle, 2, vertex_shader_with_version, nullptr); glCompileShader(m_vert_handle); - check_shader(m_vert_handle, "vertex shader"); + wxASSERT(check_shader(m_vert_handle, "vertex shader")); const GLchar* fragment_shader_with_version[2] = { m_glsl_version_string.c_str(), fragment_shader }; m_frag_handle = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(m_frag_handle, 2, fragment_shader_with_version, nullptr); glCompileShader(m_frag_handle); - check_shader(m_frag_handle, "fragment shader"); + wxASSERT(check_shader(m_frag_handle, "fragment shader")); m_shader_handle = glCreateProgram(); glAttachShader(m_shader_handle, m_vert_handle); glAttachShader(m_shader_handle, m_frag_handle); glLinkProgram(m_shader_handle); - check_program(m_shader_handle, "shader program"); + wxASSERT(check_program(m_shader_handle, "shader program")); m_attrib_location_tex = glGetUniformLocation(m_shader_handle, "Texture"); m_attrib_location_proj_mtx = glGetUniformLocation(m_shader_handle, "ProjMtx"); @@ -351,36 +393,40 @@ bool ImGuiWrapper::check_program(unsigned int handle, const char* desc) GLint status = 0, log_length = 0; glGetProgramiv(handle, GL_LINK_STATUS, &status); glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); - if ((GLboolean)status == GL_FALSE) - fprintf(stderr, "ERROR: ImGuiWrapper::check_program(): failed to link %s! (with GLSL '%s')\n", desc, m_glsl_version_string); - if (log_length > 0) - { - ImVector buf; - buf.resize((int)(log_length + 1)); - glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); - fprintf(stderr, "%s\n", buf.begin()); + + if (status == GL_FALSE) { + BOOST_LOG_TRIVIAL(error) << boost::format("ImGuiWrapper::check_program(): failed to link %1% (GLSL `%1%`)") % desc, m_glsl_version_string; } - return (GLboolean)status == GL_TRUE; + + if (log_length > 0) { + std::vector buf(log_length + 1, 0); + glGetProgramInfoLog(handle, log_length, nullptr, buf.data()); + BOOST_LOG_TRIVIAL(error) << boost::format("ImGuiWrapper::check_program(): error log:\n%1%\n") % buf.data(); + } + + return status == GL_TRUE; } -bool ImGuiWrapper::check_shader(unsigned int handle, const char* desc) +bool ImGuiWrapper::check_shader(unsigned int handle, const char *desc) { GLint status = 0, log_length = 0; glGetShaderiv(handle, GL_COMPILE_STATUS, &status); glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); - if ((GLboolean)status == GL_FALSE) - fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); - if (log_length > 0) - { - ImVector buf; - buf.resize((int)(log_length + 1)); - glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); - fprintf(stderr, "%s\n", buf.begin()); + + if (status == GL_FALSE) { + BOOST_LOG_TRIVIAL(error) << boost::format("ImGuiWrapper::check_shader(): failed to compile %1%") % desc; } - return (GLboolean)status == GL_TRUE; + + if (log_length > 0) { + std::vector buf(log_length + 1, 0); + glGetProgramInfoLog(handle, log_length, nullptr, buf.data()); + BOOST_LOG_TRIVIAL(error) << boost::format("ImGuiWrapper::check_program(): error log:\n%1%\n") % buf.data(); + } + + return status == GL_TRUE; } -void ImGuiWrapper::render_draw_data(ImDrawData* draw_data) +void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index ce795d954..aae7391d4 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -4,9 +4,13 @@ #include #include +#include + +#include "libslic3r/Point.hpp" + +class wxString; class wxMouseEvent; -class ImFont; -class ImDrawData; + namespace Slic3r { namespace GUI { @@ -30,14 +34,16 @@ class ImGuiWrapper FontsMap m_fonts; unsigned int m_font_texture; + unsigned m_mouse_buttons; + public: ImGuiWrapper(); + ~ImGuiWrapper(); bool init(); - void shutdown(); void set_display_size(float w, float h); - void update_mouse_data(wxMouseEvent& evt); + bool update_mouse_data(wxMouseEvent &evt); void new_frame(); void render(); @@ -45,19 +51,25 @@ public: void set_next_window_pos(float x, float y, int flag); void set_next_window_bg_alpha(float alpha); - bool begin(const std::string& name, int flags = 0); + bool begin(const std::string &name, int flags = 0); + bool begin(const wxString &name, int flags = 0); void end(); - bool input_double(const std::string& label, double& value, const std::string& format = "%.3f"); - - bool input_vec3(const std::string& label, Vec3d& value, float width, const std::string& format = "%.3f"); + bool button(const wxString &label); + bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); + bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); + bool checkbox(const wxString &label, bool &value); + bool want_mouse() const; + bool want_keyboard() const; + bool want_text_input() const; + bool want_any_input() const; private: void create_device_objects(); void create_fonts_texture(); - bool check_program(unsigned int handle, const char* desc); - bool check_shader(unsigned int handle, const char* desc); - void render_draw_data(ImDrawData* draw_data); + bool check_program(unsigned int handle, const char *desc); + bool check_shader(unsigned int handle, const char *desc); + void render_draw_data(ImDrawData *draw_data); void destroy_device_objects(); void destroy_fonts_texture(); }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d4b4f1e1c..6edf345b5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -913,7 +913,9 @@ struct Plater::priv // GUI elements wxNotebook *notebook; Sidebar *sidebar; +#ifndef ENABLE_IMGUI wxPanel *panel3d; +#endif // not ENABLE_IMGUI wxGLCanvas *canvas3Dwidget; // TODO: Use GLCanvas3D when we can GLCanvas3D *canvas3D; Preview *preview; @@ -1039,8 +1041,12 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) })) , notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)) , sidebar(new Sidebar(q)) +#ifdef ENABLE_IMGUI + , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(notebook)) +#else , panel3d(new wxPanel(notebook, wxID_ANY)) , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(panel3d)) +#endif // ENABLE_IMGUI , canvas3D(nullptr) , delayed_scene_refresh(false) #if ENABLE_NEW_MENU_LAYOUT @@ -1068,6 +1074,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->canvas3D = _3DScene::get_canvas(this->canvas3Dwidget); this->canvas3D->allow_multisample(GLCanvas3DManager::can_multisample()); +#ifdef ENABLE_IMGUI + notebook->AddPage(canvas3Dwidget, _(L("3D"))); +#else auto *panel3dsizer = new wxBoxSizer(wxVERTICAL); panel3dsizer->Add(canvas3Dwidget, 1, wxEXPAND); auto *panel_gizmo_widgets = new wxPanel(panel3d, wxID_ANY); @@ -1076,9 +1085,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) panel3d->SetSizer(panel3dsizer); notebook->AddPage(panel3d, _(L("3D"))); - preview = new GUI::Preview(notebook, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); canvas3D->set_external_gizmo_widgets_parent(panel_gizmo_widgets); +#endif // ENABLE_IMGUI + + preview = new GUI::Preview(notebook, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); // XXX: If have OpenGL this->canvas3D->enable_picking(true); @@ -1921,7 +1932,11 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) { const auto current_id = notebook->GetCurrentPage()->GetId(); +#ifdef ENABLE_IMGUI + if (current_id == canvas3Dwidget->GetId()) { +#else if (current_id == panel3d->GetId()) { +#endif // ENABLE_IMGUI if (this->canvas3D->is_reload_delayed()) { // Delayed loading of the 3D scene. if (this->printer_technology == ptSLA) { @@ -2454,14 +2469,14 @@ bool Plater::is_selection_empty() const return p->get_selection().is_empty(); } -void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z) +void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper, bool keep_lower, bool rotate_lower) { wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); auto *object = p->model.objects[obj_idx]; wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - const auto new_objects = object->cut(instance_idx, z); + const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower); remove(obj_idx); p->load_model_objects(new_objects); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 87d85cbb4..04b0523f8 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -139,7 +139,7 @@ public: void set_number_of_copies(/*size_t num*/); bool is_selection_empty() const; - void cut(size_t obj_idx, size_t instance_idx, coordf_t z); + void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: empty path means "use the default" void export_gcode(boost::filesystem::path output_path = boost::filesystem::path());