From f7284a6569182ae2a0cfee6dd96100a25551531c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 27 Jan 2022 17:23:10 +0100 Subject: [PATCH 01/93] Cut: Extensions for Dialog of CutGizmo --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 155 ++++++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 55 ++++++++++ 2 files changed, 194 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index ff5d89f5e..3d5d1cd48 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -27,7 +27,22 @@ static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + m_modes = { _u8L("Planar"), _u8L("Grid") +// , _u8L("Radial"), _u8L("Modular") + }; + + m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; + + m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") +// , _u8L("Claw") + }; + + m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") +// , _u8L("D-shape") + }; + +} std::string GLGizmoCut::get_tooltip() const { @@ -220,6 +235,86 @@ void GLGizmoCut::on_render_for_picking() render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); } +void GLGizmoCut::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) +{ + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width); + + size_t selection_out = selection_idx; + // It is necessary to use BeginGroup(). Otherwise, when using SameLine() is called, then other items will be drawn inside the combobox. + ImGui::BeginGroup(); + ImVec2 combo_pos = ImGui::GetCursorScreenPos(); + if (ImGui::BeginCombo(("##"+label).c_str(), "")) { + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ImGui::PushID(int(line_idx)); + ImVec2 start_position = ImGui::GetCursorScreenPos(); + + if (ImGui::Selectable("", line_idx == selection_idx)) + selection_out = line_idx; + + ImGui::SameLine(); + ImGui::Text("%s", lines[line_idx].c_str()); + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + + ImVec2 backup_pos = ImGui::GetCursorScreenPos(); + ImGuiStyle& style = ImGui::GetStyle(); + + ImGui::SetCursorScreenPos(ImVec2(combo_pos.x + style.FramePadding.x, combo_pos.y + style.FramePadding.y)); + ImGui::Text("%s", lines[selection_out].c_str()); + ImGui::SetCursorScreenPos(backup_pos); + ImGui::EndGroup(); + + selection_idx = selection_out; +} + +void GLGizmoCut::render_double_input(const std::string& label, double& value_in) +{ + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width); + + double value = value_in; + if (m_imperial_units) + value *= ObjectManipulation::mm_to_in; + ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + + ImGui::SameLine(); + m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + + value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); +} + +void GLGizmoCut::render_rotation_input(const std::string& label, double& value_in) +{ + m_imgui->text(label); + ImGui::SameLine(); + + double value = value_in; + if (value > 360) + value -= 360; + + ImGui::PushItemWidth(0.3*m_control_width); + ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + + value_in = value; +} + +void GLGizmoCut::render_radio_button(ConnectorType type) +{ + ImGui::SameLine(type == ConnectorType::Plug ? m_label_width : 2*m_label_width); + ImGui::PushItemWidth(m_control_width); + if (m_imgui->radio_button(m_connector_types[int(type)], m_connector_type == type)) + m_connector_type = type; +} + + void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) { static float last_y = 0.0f; @@ -227,7 +322,9 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - const bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + m_label_width = m_imgui->get_style_scaling() * 100.0f; + m_control_width = m_imgui->get_style_scaling() * 150.0f; // adjust window position to avoid overlap the view toolbar const float win_h = ImGui::GetWindowHeight(); @@ -242,26 +339,52 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) last_y = y; } - ImGui::AlignTextToFramePadding(); - m_imgui->text("Z"); - ImGui::SameLine(); - ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f); + render_combo(_u8L("Mode"), m_modes, m_mode); - double cut_z = m_cut_z; - if (imperial_units) - cut_z *= ObjectManipulation::mm_to_in; - ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + if (m_mode == CutMode::cutPlanar) { + ImGui::Separator(); - ImGui::SameLine(); - m_imgui->text(imperial_units ? _L("in") : _L("mm")); + render_double_input("Z", m_cut_z); - m_cut_z = cut_z * (imperial_units ? ObjectManipulation::in_to_mm : 1.0); + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Rotation")); + ImGui::SameLine(m_label_width); + render_rotation_input("X:", m_rotation_x); + ImGui::SameLine(); + render_rotation_input("Y:", m_rotation_y); + ImGui::SameLine(); + render_rotation_input("Z:", m_rotation_z); + + ImGui::SameLine(); + m_imgui->text(_L("°")); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("After cut")); + ImGui::SameLine(m_label_width); + m_imgui->checkbox(_L("Keep upper part"), m_keep_upper); + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->checkbox(_L("Keep lower part"), m_keep_lower); + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->disabled_begin(!m_keep_lower); + m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); + m_imgui->disabled_end(); + } + + // Connectors section ImGui::Separator(); - 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); + m_imgui->text(_L("Connectors")); + render_radio_button(ConnectorType::Plug); + render_radio_button(ConnectorType::Dowel); + + render_combo(_u8L("Style"), m_connector_styles, m_connector_style); + render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape); + + render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio); + render_double_input(_u8L("Size"), m_connector_size); ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ccf8732cf..14f305c03 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -15,6 +15,9 @@ class GLGizmoCut : public GLGizmoBase static const double Margin; double m_cut_z{ 0.0 }; + double m_rotation_x{ 0.0 }; + double m_rotation_y{ 0.0 }; + double m_rotation_z{ 0.0 }; double m_max_z{ 0.0 }; double m_start_z{ 0.0 }; Vec3d m_drag_pos; @@ -27,6 +30,53 @@ class GLGizmoCut : public GLGizmoBase GLModel m_grabber_connection; float m_old_z{ 0.0f }; #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + double m_connector_depth_ratio{ 1.5 }; + double m_connector_size{ 5.0 }; + + float m_label_width{ 150.0 }; + float m_control_width{ 200.0 }; + bool m_imperial_units{ false }; + +public: + enum CutMode { + cutPlanar = 0 + ,cutGrig + //,cutRadial + //,cutModular + }; + + enum ConnectorType { + Plug = 0 + ,Dowel + }; + + enum ConnectorStyle { + Prizm = 0 + ,Frustrum + //,Claw + }; + + enum ConnectorShape { + Triangle = 0 + ,Square + ,Circle + ,Hexagon +// ,D-shape + }; + +private: + + std::vector m_modes; + size_t m_mode{ size_t(cutPlanar) }; + + std::vector m_connector_types; + ConnectorType m_connector_type{ Plug }; + + std::vector m_connector_styles; + size_t m_connector_style{ size_t(Prizm) }; + + std::vector m_connector_shapes; + size_t m_connector_shape{ size_t(Hexagon) }; struct CutContours { @@ -63,6 +113,11 @@ protected: virtual void on_render_for_picking() override; virtual void on_render_input_window(float x, float y, float bottom_limit) override; + void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); + void render_double_input(const std::string& label, double& value_in); + void render_rotation_input(const std::string& label, double& value_in); + void render_radio_button(ConnectorType type); + private: void perform_cut(const Selection& selection); double calc_projection(const Linef3& mouse_ray) const; From 76784441be56794478948c23f02e5ee174d01d34 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 9 Feb 2022 16:11:53 +0100 Subject: [PATCH 02/93] Cut: next UI improvements --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 522 +++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 195 +++++--- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 9 + src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 3 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- 5 files changed, 559 insertions(+), 172 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3d5d1cd48..3688b10f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -28,20 +28,6 @@ static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_modes = { _u8L("Planar"), _u8L("Grid") -// , _u8L("Radial"), _u8L("Modular") - }; - - m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; - - m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") -// , _u8L("Claw") - }; - - m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") -// , _u8L("D-shape") - }; - } std::string GLGizmoCut::get_tooltip() const @@ -56,7 +42,7 @@ std::string GLGizmoCut::get_tooltip() const bool GLGizmoCut::on_init() { m_grabbers.emplace_back(); - m_shortcut_key = WXK_CONTROL_C; +// m_shortcut_key = WXK_CONTROL_C; return true; } @@ -68,7 +54,7 @@ std::string GLGizmoCut::on_get_name() const void GLGizmoCut::on_set_state() { // Reset m_cut_z on gizmo activation - if (get_state() == On) + if (get_state() == On && m_cut_z == 0.0) m_cut_z = bounding_box().center().z(); } @@ -107,14 +93,20 @@ void GLGizmoCut::on_render() update_contours(); - const float min_x = box.min.x() - Margin; - const float max_x = box.max.x() + Margin; - const float min_y = box.min.y() - Margin; - const float max_y = box.max.y() + Margin; - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_CULL_FACE)); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + //const float min_x = box.min.x() - Margin - plane_center.x(); + //const float max_x = box.max.x() + Margin - plane_center.x(); + //const float min_y = box.min.y() - Margin - plane_center.y(); + //const float max_y = box.max.y() + Margin - plane_center.y(); +// glsafe(::glEnable(GL_DEPTH_TEST)); +// glsafe(::glDisable(GL_CULL_FACE)); +// glsafe(::glEnable(GL_BLEND)); +// glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); +// +//#if ENABLE_GLBEGIN_GLEND_REMOVAL +//#endif // ENABLE_GLBEGIN_GLEND_REMOVAL +// +// glsafe(::glEnable(GL_CULL_FACE)); +// glsafe(::glDisable(GL_BLEND)); #if ENABLE_GLBEGIN_GLEND_REMOVAL GLShaderProgram* shader = wxGetApp().get_shader("flat"); @@ -229,13 +221,80 @@ void GLGizmoCut::on_render() #endif // ENABLE_GLBEGIN_GLEND_REMOVAL } + glsafe(::glPushMatrix()); + glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); +// glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); + glsafe(::glRotated(Geometry::rad2deg(m_angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(m_angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(m_angles.x()), 1.0, 0.0, 0.0)); + + glsafe(::glLineWidth(2.0f)); + m_cut_contours.contours.render(); + glsafe(::glPopMatrix()); +} + void GLGizmoCut::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); } -void GLGizmoCut::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) +void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) +{ + static float last_y = 0.0f; + static float last_h = 0.0f; + + m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + const bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + + // adjust window position to avoid overlap the view toolbar + const float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + if (last_h != win_h || last_y != y) { + // ask canvas for another frame to render the window in the correct position + m_imgui->set_requires_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + + ImGui::AlignTextToFramePadding(); + m_imgui->text("Z"); + ImGui::SameLine(); + ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f); + + double cut_z = m_cut_z; + if (imperial_units) + cut_z *= ObjectManipulation::mm_to_in; + ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + + ImGui::SameLine(); + m_imgui->text(imperial_units ? _L("in") : _L("mm")); + + m_cut_z = cut_z * (imperial_units ? ObjectManipulation::in_to_mm : 1.0); + + ImGui::Separator(); + + 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); + + ImGui::Separator(); + + m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || m_cut_z <= 0.0 || m_max_z <= m_cut_z); + const bool cut_clicked = m_imgui->button(_L("Perform cut")); + m_imgui->disabled_end(); + + m_imgui->end(); + + if (cut_clicked && (m_keep_upper || m_keep_lower)) + perform_cut(m_parent.get_selection()); +} + +void GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) { ImGui::AlignTextToFramePadding(); m_imgui->text(label); @@ -273,7 +332,7 @@ void GLGizmoCut::render_combo(const std::string& label, const std::vectortext(label); @@ -291,22 +350,46 @@ void GLGizmoCut::render_double_input(const std::string& label, double& value_in) value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); } -void GLGizmoCut::render_rotation_input(const std::string& label, double& value_in) +void GLGizmoCut3D::render_move_center_input(int axis) { - m_imgui->text(label); + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_axis_names[axis]+":"); + ImGui::SameLine(); + ImGui::PushItemWidth(0.3*m_control_width); + + double value = axis == Z ? m_cut_plane_gizmo.get_cut_z() : 0.0; + if (m_imperial_units) + value *= ObjectManipulation::mm_to_in; + ImGui::InputDouble(("##move_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - double value = value_in; + if (axis == Z) { + double val = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); + m_cut_plane_gizmo.set_cut_z(val); + set_center_z(val); + } +} + +void GLGizmoCut3D::render_rotation_input(int axis) +{ + m_imgui->text(m_axis_names[axis] + ":"); + ImGui::SameLine(); + + Vec3d rotation = get_rotation(); + double value = rotation[axis]; if (value > 360) value -= 360; ImGui::PushItemWidth(0.3*m_control_width); - ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + ImGui::SameLine(); - value_in = value; + rotation[axis] = value; + set_rotation(rotation); + m_cut_plane_gizmo.set_angles(rotation); } -void GLGizmoCut::render_radio_button(ConnectorType type) +void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type) { ImGui::SameLine(type == ConnectorType::Plug ? m_label_width : 2*m_label_width); ImGui::PushItemWidth(m_control_width); @@ -314,88 +397,93 @@ void GLGizmoCut::render_radio_button(ConnectorType type) m_connector_type = type; } - -void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) +void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode) { - static float last_y = 0.0f; - static float last_h = 0.0f; + ImGui::SameLine(mode == ConnectorMode::Auto ? m_label_width : 2*m_label_width); + ImGui::PushItemWidth(m_control_width); + if (m_imgui->radio_button(m_connector_modes[int(mode)], m_connector_mode == mode)) + m_connector_mode = mode; +} - m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); +void GLGizmoCut3D::render_cut_plane() +{ + const BoundingBoxf3 box = m_cut_plane_gizmo.bounding_box(); + Vec3d plane_center = box.center(); + plane_center.z() = m_cut_plane_gizmo.get_cut_z(); - m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - m_label_width = m_imgui->get_style_scaling() * 100.0f; - m_control_width = m_imgui->get_style_scaling() * 150.0f; + const float min_x = box.min.x() - GLGizmoCut::Margin - plane_center.x(); + const float max_x = box.max.x() + GLGizmoCut::Margin - plane_center.x(); + const float min_y = box.min.y() - GLGizmoCut::Margin - plane_center.y(); + const float max_y = box.max.y() + GLGizmoCut::Margin - plane_center.y(); + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - // adjust window position to avoid overlap the view toolbar - const float win_h = ImGui::GetWindowHeight(); - y = std::min(y, bottom_limit - win_h); - ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - if (last_h != win_h || last_y != y) { - // ask canvas for another frame to render the window in the correct position - m_imgui->set_requires_extra_frame(); - if (last_h != win_h) - last_h = win_h; - if (last_y != y) - last_y = y; +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + shader->start_using(); + +// bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON; +// m_old_z = plane_center.z(); + + Vec3d angles = get_rotation(); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); + glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); + + if (!m_plane.is_initialized()/* || z_changed*/) { + m_plane.reset(); + + GLModel::InitializationData init_data; + GLModel::InitializationData::Entity entity; + entity.type = GLModel::PrimitiveType::Triangles; + entity.positions.reserve(4); + entity.positions.emplace_back(Vec3f(min_x, min_y, 0.0)); + entity.positions.emplace_back(Vec3f(max_x, min_y, 0.0)); + entity.positions.emplace_back(Vec3f(max_x, max_y, 0.0)); + entity.positions.emplace_back(Vec3f(min_x, max_y, 0.0)); + + entity.normals.reserve(4); + for (size_t i = 0; i < 4; ++i) { + entity.normals.emplace_back(Vec3f::UnitZ()); + } + + entity.indices.reserve(6); + entity.indices.emplace_back(0); + entity.indices.emplace_back(1); + entity.indices.emplace_back(2); + entity.indices.emplace_back(2); + entity.indices.emplace_back(3); + entity.indices.emplace_back(0); + + init_data.entities.emplace_back(entity); + m_plane.init_from(init_data); + m_plane.set_color(-1, PLANE_COLOR); } - render_combo(_u8L("Mode"), m_modes, m_mode); + m_plane.render(); + glsafe(::glPopMatrix()); +#else + // Draw the cutting plane + ::glBegin(GL_QUADS); + ::glColor4fv(PLANE_COLOR.data()); + ::glVertex3f(min_x, min_y, plane_center.z()); + ::glVertex3f(max_x, min_y, plane_center.z()); + ::glVertex3f(max_x, max_y, plane_center.z()); + ::glVertex3f(min_x, max_y, plane_center.z()); + glsafe(::glEnd()); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - if (m_mode == CutMode::cutPlanar) { - ImGui::Separator(); + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); - render_double_input("Z", m_cut_z); - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Rotation")); - ImGui::SameLine(m_label_width); - - render_rotation_input("X:", m_rotation_x); - ImGui::SameLine(); - render_rotation_input("Y:", m_rotation_y); - ImGui::SameLine(); - render_rotation_input("Z:", m_rotation_z); - - ImGui::SameLine(); - m_imgui->text(_L("°")); - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut")); - ImGui::SameLine(m_label_width); - m_imgui->checkbox(_L("Keep upper part"), m_keep_upper); - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->checkbox(_L("Keep lower part"), m_keep_lower); - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->disabled_begin(!m_keep_lower); - m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); - m_imgui->disabled_end(); - } - - // Connectors section - ImGui::Separator(); - - m_imgui->text(_L("Connectors")); - render_radio_button(ConnectorType::Plug); - render_radio_button(ConnectorType::Dowel); - - render_combo(_u8L("Style"), m_connector_styles, m_connector_style); - render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape); - - render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio); - render_double_input(_u8L("Size"), m_connector_size); - - ImGui::Separator(); - - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || m_cut_z <= 0.0 || m_max_z <= m_cut_z); - const bool cut_clicked = m_imgui->button(_L("Perform cut")); - m_imgui->disabled_end(); - - m_imgui->end(); - - if (cut_clicked && (m_keep_upper || m_keep_lower)) - perform_cut(m_parent.get_selection()); + shader->stop_using(); } void GLGizmoCut::set_cut_z(double cut_z) @@ -481,18 +569,28 @@ void GLGizmoCut::update_contours() if (m_cut_contours.object_id != model_object->id() || m_cut_contours.volumes_idxs != volumes_idxs) m_cut_contours.mesh = model_object->raw_mesh(); - m_cut_contours.position = box.center(); - m_cut_contours.shift = Vec3d::Zero(); + m_cut_contours.position = Vec3d::Zero();//box.center(); + m_cut_contours.shift = box.center();//Vec3d::Zero(); m_cut_contours.object_id = model_object->id(); m_cut_contours.instance_idx = instance_idx; m_cut_contours.volumes_idxs = volumes_idxs; m_cut_contours.contours.reset(); + // addition back transformation + Geometry::Transformation m_transformation = Geometry::Transformation(); + m_transformation.set_offset(-first_glvolume->get_instance_transformation().get_offset()); + m_transformation.set_rotation(-m_angles); + auto cut_params = m_transformation.get_matrix(); + + MeshSlicingParams slicing_params; - slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix(); - const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params); + slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix() * cut_params; + + auto cut_z = m_cut_z - box.center().z(); + + const Polygons polys = slice_mesh(m_cut_contours.mesh.its, /*m_*/cut_z, slicing_params); if (!polys.empty()) { - m_cut_contours.contours.init_from(polys, static_cast(m_cut_z)); + m_cut_contours.contours.init_from(polys, static_cast(/*m_*/cut_z)); #if ENABLE_GLBEGIN_GLEND_REMOVAL m_cut_contours.contours.set_color(ColorRGBA::WHITE()); #else @@ -500,13 +598,211 @@ void GLGizmoCut::update_contours() #endif // ENABLE_GLBEGIN_GLEND_REMOVAL } } - else if (box.center() != m_cut_contours.position) { - m_cut_contours.shift = box.center() - m_cut_contours.position; + else if (box.center() != m_cut_contours.shift) { + m_cut_contours.shift = box.center();// -m_cut_contours.position; } + //else if (box.center() != m_cut_contours.position) { + // m_cut_contours.shift = -box.center();// -m_cut_contours.position; + //} } else m_cut_contours.contours.reset(); } +GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoRotate3D(parent, icon_filename, sprite_id) + , m_cut_plane_gizmo(GLGizmoCut(parent, "", -1)) +{ + m_cut_plane_gizmo.set_group_id(3); + + m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") +// , _u8L("Radial"), _u8L("Modular") + }; + + m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; + m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; + + m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") +// , _u8L("Claw") + }; + + m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") +// , _u8L("D-shape") + }; + + m_axis_names = {"X", "Y", "Z"}; +} + +bool GLGizmoCut3D::on_init() +{ + if(!GLGizmoRotate3D::on_init()) + return false; + + if (!m_cut_plane_gizmo.init()) + return false; + + m_shortcut_key = WXK_CONTROL_C; + return true; +} + +std::string GLGizmoCut3D::on_get_name() const +{ + return _u8L("Cut"); +} + +bool GLGizmoCut3D::on_is_activable() const +{ + return m_cut_plane_gizmo.is_activable(); +} + +void GLGizmoCut3D::on_start_dragging() +{ + GLGizmoRotate3D::on_start_dragging(); + if (m_hover_id == 3) + m_cut_plane_gizmo.start_dragging(); +} + +void GLGizmoCut3D::on_stop_dragging() +{ + GLGizmoRotate3D::on_stop_dragging(); + if (m_hover_id == 3) + m_cut_plane_gizmo.stop_dragging(); +} + +void GLGizmoCut3D::on_render() +{ + render_cut_plane(); + if (m_mode == CutMode::cutPlanar) { + GLGizmoRotate3D::on_render(); +// if (m_hover_id == -1 || m_hover_id == 3) + } + m_cut_plane_gizmo.render(); +} + +void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) +{ + static float last_y = 0.0f; + static float last_h = 0.0f; + + m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + m_label_width = m_imgui->get_style_scaling() * 100.0f; + m_control_width = m_imgui->get_style_scaling() * 150.0f; + + // adjust window position to avoid overlap the view toolbar + const float win_h = ImGui::GetWindowHeight(); + y = std::min(y, bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + if (last_h != win_h || last_y != y) { + // ask canvas for another frame to render the window in the correct position + m_imgui->set_requires_extra_frame(); + if (last_h != win_h) + last_h = win_h; + if (last_y != y) + last_y = y; + } + + render_combo(_u8L("Mode"), m_modes, m_mode); + + if (m_mode <= CutMode::cutByLine) { + ImGui::Separator(); + + if (m_mode == CutMode::cutPlanar) { + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Move center")); + ImGui::SameLine(m_label_width); + for (Axis axis : {X, Y, Z}) + render_move_center_input(axis); + m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Rotation")); + ImGui::SameLine(m_label_width); + for (Axis axis : {X, Y, Z}) + render_rotation_input(axis); + m_imgui->text(_L("°")); + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::AlignTextToFramePadding(); + ImGui::AlignTextToFramePadding(); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 3*m_control_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connect some two points of object to cteate a cut plane")); + ImGui::PopTextWrapPos(); + ImGui::AlignTextToFramePadding(); + ImGui::AlignTextToFramePadding(); + } + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("After cut")); + ImGui::SameLine(m_label_width); + m_imgui->checkbox(_L("Keep upper part"), m_keep_upper); + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->checkbox(_L("Keep lower part"), m_keep_lower); + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->disabled_begin(!m_keep_lower); + m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); + m_imgui->disabled_end(); + } + + m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); + // Connectors section + ImGui::Separator(); + + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); + + m_imgui->text(_L("Mode")); + render_connect_mode_radio_button(ConnectorMode::Auto); + render_connect_mode_radio_button(ConnectorMode::Manual); + + m_imgui->text(_L("Type")); + render_connect_type_radio_button(ConnectorType::Plug); + render_connect_type_radio_button(ConnectorType::Dowel); + + render_combo(_u8L("Style"), m_connector_styles, m_connector_style); + render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape); + + render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio); + render_double_input(_u8L("Size"), m_connector_size); + + m_imgui->disabled_end(); + + ImGui::Separator(); + + m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) /*|| m_cut_z <= 0.0 || m_max_z <= m_cut_z*/); + const bool cut_clicked = m_imgui->button(_L("Perform cut")); + m_imgui->disabled_end(); + + m_imgui->end(); + + if (cut_clicked && (m_keep_upper || m_keep_lower)) + perform_cut(m_parent.get_selection()); +} + +void GLGizmoCut3D::perform_cut(const Selection& selection) +{ + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + + wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); + + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. + const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); + const double object_cut_z = m_cut_plane_gizmo.get_cut_z() - first_glvolume->get_sla_shift_z(); + + if (0.0 < object_cut_z/* && object_cut_z < m_max_z*/) + wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z, + only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); + else { + // the object is SLA-elevated and the plane is under it. + } +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 14f305c03..f8d7570a4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -2,6 +2,7 @@ #define slic3r_GLGizmoCut_hpp_ #include "GLGizmoBase.hpp" +#include "GLGizmoRotate.hpp" #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/ObjectID.hpp" @@ -11,13 +12,11 @@ namespace GUI { class GLGizmoCut : public GLGizmoBase { +public: static const double Offset; static const double Margin; - +private: double m_cut_z{ 0.0 }; - double m_rotation_x{ 0.0 }; - double m_rotation_y{ 0.0 }; - double m_rotation_z{ 0.0 }; double m_max_z{ 0.0 }; double m_start_z{ 0.0 }; Vec3d m_drag_pos; @@ -26,57 +25,11 @@ class GLGizmoCut : public GLGizmoBase bool m_keep_lower{ true }; bool m_rotate_lower{ false }; #if ENABLE_GLBEGIN_GLEND_REMOVAL - GLModel m_plane; GLModel m_grabber_connection; float m_old_z{ 0.0f }; #endif // ENABLE_GLBEGIN_GLEND_REMOVAL - double m_connector_depth_ratio{ 1.5 }; - double m_connector_size{ 5.0 }; - float m_label_width{ 150.0 }; - float m_control_width{ 200.0 }; - bool m_imperial_units{ false }; - -public: - enum CutMode { - cutPlanar = 0 - ,cutGrig - //,cutRadial - //,cutModular - }; - - enum ConnectorType { - Plug = 0 - ,Dowel - }; - - enum ConnectorStyle { - Prizm = 0 - ,Frustrum - //,Claw - }; - - enum ConnectorShape { - Triangle = 0 - ,Square - ,Circle - ,Hexagon -// ,D-shape - }; - -private: - - std::vector m_modes; - size_t m_mode{ size_t(cutPlanar) }; - - std::vector m_connector_types; - ConnectorType m_connector_type{ Plug }; - - std::vector m_connector_styles; - size_t m_connector_style{ size_t(Prizm) }; - - std::vector m_connector_shapes; - size_t m_connector_shape{ size_t(Hexagon) }; + Vec3d m_angles{ Vec3d::Zero() }; struct CutContours { @@ -95,8 +48,9 @@ private: public: GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - double get_cut_z() const { return m_cut_z; } - void set_cut_z(double cut_z); + double get_cut_z() const { return m_cut_z; } + void set_cut_z(double cut_z); + void set_angles(const Vec3d& angles) { m_angles = angles; } std::string get_tooltip() const override; @@ -113,18 +67,143 @@ protected: virtual void on_render_for_picking() override; virtual void on_render_input_window(float x, float y, float bottom_limit) override; - void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); - void render_double_input(const std::string& label, double& value_in); - void render_rotation_input(const std::string& label, double& value_in); - void render_radio_button(ConnectorType type); - private: void perform_cut(const Selection& selection); double calc_projection(const Linef3& mouse_ray) const; + +public: BoundingBoxf3 bounding_box() const; void update_contours(); }; +class GLGizmoCut3D : public GLGizmoRotate3D +{ + GLGizmoCut m_cut_plane_gizmo; + +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLModel m_plane; + float m_old_z{ 0.0f }; +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + + bool m_keep_upper{ true }; + bool m_keep_lower{ true }; + bool m_rotate_lower{ false }; + + double m_connector_depth_ratio{ 1.5 }; + double m_connector_size{ 5.0 }; + + float m_label_width{ 150.0 }; + float m_control_width{ 200.0 }; + bool m_imperial_units{ false }; + + enum CutMode { + cutPlanar + , cutByLine + , cutGrig + //,cutRadial + //,cutModular + }; + + enum ConnectorMode { + Auto + , Manual + }; + + enum ConnectorType { + Plug + , Dowel + }; + + enum ConnectorStyle { + Prizm + , Frustrum + //,Claw + }; + + enum ConnectorShape { + Triangle + , Square + , Circle + , Hexagon + //,D-shape + }; + + std::vector m_modes; + size_t m_mode{ size_t(cutPlanar) }; + + std::vector m_connector_modes; + ConnectorMode m_connector_mode{ Auto }; + + std::vector m_connector_types; + ConnectorType m_connector_type{ Plug }; + + std::vector m_connector_styles; + size_t m_connector_style{ size_t(Prizm) }; + + std::vector m_connector_shapes; + size_t m_connector_shape{ size_t(Hexagon) }; + + std::vector m_axis_names; + +public: + GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + std::string get_tooltip() const override { + std::string tooltip = GLGizmoRotate3D::get_tooltip(); + if (tooltip.empty()) + tooltip = m_cut_plane_gizmo.get_tooltip(); + return tooltip; + } + +protected: + bool on_init() override; + std::string on_get_name() const override; + void on_set_state() override { + GLGizmoRotate3D::on_set_state(); + m_cut_plane_gizmo.set_state(m_state); + } + void on_set_hover_id() override { + GLGizmoRotate3D::on_set_hover_id(); + m_cut_plane_gizmo.set_hover_id((m_hover_id == 3) ? 0 : -1); + } + void on_enable_grabber(unsigned int id) override { + GLGizmoRotate3D::on_enable_grabber(id); + if (id == 3) + m_cut_plane_gizmo.enable_grabber(0); + } + void on_disable_grabber(unsigned int id) override { + GLGizmoRotate3D::on_disable_grabber(id); + if (id == 3) + m_cut_plane_gizmo.disable_grabber(0); + } + bool on_is_activable() const override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_update(const UpdateData& data) override { + GLGizmoRotate3D::on_update(data); + m_cut_plane_gizmo.update(data); + } + void on_render() override; + void on_render_for_picking() override { + GLGizmoRotate3D::on_render_for_picking(); + m_cut_plane_gizmo.render_for_picking(); + } + + void on_render_input_window(float x, float y, float bottom_limit) override; + +private: + + void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); + void render_double_input(const std::string& label, double& value_in); + void render_move_center_input(int axis); + void render_rotation_input(int axis); + void render_connect_mode_radio_button(ConnectorMode mode); + void render_connect_type_radio_button(ConnectorType type); + + void render_cut_plane(); + void perform_cut(const Selection& selection); +}; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index dca578bd7..9869634cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -38,6 +38,11 @@ void GLGizmoRotate::set_angle(double angle) m_angle = angle; } +void GLGizmoRotate::set_center_z(double center_z) +{ + m_center_z = center_z; +} + std::string GLGizmoRotate::get_tooltip() const { std::string axis; @@ -60,6 +65,8 @@ void GLGizmoRotate::on_start_dragging() { const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); m_center = box.center(); + if (m_center_z >= 0) + m_center[Z] = m_center_z; m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; @@ -112,6 +119,8 @@ void GLGizmoRotate::on_render() if (m_hover_id != 0 && !m_grabbers.front().dragging) { m_center = box.center(); + if (m_center_z >= 0) + m_center[Z] = m_center_z; m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index bb33e0f73..0c291cbef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -28,6 +28,7 @@ public: private: Axis m_axis; double m_angle{ 0.0 }; + double m_center_z{ -1.0 }; Vec3d m_center{ Vec3d::Zero() }; float m_radius{ 0.0f }; float m_snap_coarse_in_radius{ 0.0f }; @@ -58,6 +59,7 @@ public: double get_angle() const { return m_angle; } void set_angle(double angle); + void set_center_z(double center_z); std::string get_tooltip() const override; @@ -101,6 +103,7 @@ public: Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } + void set_center_z(double center_z) { m_gizmos[X].set_center_z(center_z); m_gizmos[Y].set_center_z(center_z); m_gizmos[Z].set_center_z(center_z); } std::string get_tooltip() const override { std::string tooltip = m_gizmos[X].get_tooltip(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 55cbb0c30..3a144a65d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -97,7 +97,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1)); m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); - m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); + m_gizmos.emplace_back(new GLGizmoCut3D(m_parent, "cut.svg", 4)); m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5)); m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6)); m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, "fdm_supports.svg", 7)); From c45c004545518e4f4e713867b78236094e99765e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 16 Feb 2022 12:36:22 +0100 Subject: [PATCH 03/93] Cut: next improvements. Rewrite GLGizmoCut3D as a new GLGizmoBase which contained GLGizmoRotation3D and GLGizmoMove3D --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 617 ++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 107 +--- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 40 +- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 6 + src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 13 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 +- 7 files changed, 234 insertions(+), 559 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3688b10f9..847a15a9d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -21,277 +21,115 @@ namespace Slic3r { namespace GUI { -const double GLGizmoCut::Offset = 10.0; -const double GLGizmoCut::Margin = 20.0; -static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); +const double GLGizmoCenterMove::Margin = 20.0; -GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoCenterMove::GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoMove3D(parent, "", -1) { } -std::string GLGizmoCut::get_tooltip() const +void GLGizmoCenterMove::set_center_pos(const Vec3d& centre_pos) { - double cut_z = m_cut_z; - if (wxGetApp().app_config->get("use_inches") == "1") - cut_z *= ObjectManipulation::mm_to_in; - - return (m_hover_id == 0 || m_grabbers[0].dragging) ? "Z: " + format(cut_z, 2) : ""; + // Clamp the center position of the cut plane to the object's bounding box + set_center(Vec3d(std::clamp(centre_pos.x(), m_min_pos.x(), m_max_pos.x()), + std::clamp(centre_pos.y(), m_min_pos.y(), m_max_pos.y()), + std::clamp(centre_pos.z(), m_min_pos.z(), m_max_pos.z()))); } -bool GLGizmoCut::on_init() +std::string GLGizmoCenterMove::get_tooltip() const { - m_grabbers.emplace_back(); -// m_shortcut_key = WXK_CONTROL_C; - return true; + double koef = wxGetApp().app_config->get("use_inches") == "1" ? ObjectManipulation::mm_to_in : 1.0; + + const Vec3d& center_pos = get_center(); + + if (m_hover_id == 0 || m_grabbers[0].dragging) + return "X: " + format(center_pos.x() * koef, 2); + else if (m_hover_id == 1 || m_grabbers[1].dragging) + return "Y: " + format(center_pos.y() * koef, 2); + else if (m_hover_id == 2 || m_grabbers[2].dragging) + return "Z: " + format(center_pos.z() * koef, 2); + else + return ""; } -std::string GLGizmoCut::on_get_name() const +void GLGizmoCenterMove::on_set_state() { - return _u8L("Cut"); + // Reset internal variables on gizmo activation, if bounding box was changed + if (get_state() == On) { + const BoundingBoxf3 box = bounding_box(); + if (m_max_pos != box.max && m_min_pos != box.min) { + m_max_pos = box.max; + m_min_pos = box.min; + set_center_pos(box.center()); + } + } } -void GLGizmoCut::on_set_state() +void GLGizmoCenterMove::on_update(const UpdateData& data) { - // Reset m_cut_z on gizmo activation - if (get_state() == On && m_cut_z == 0.0) - m_cut_z = bounding_box().center().z(); + GLGizmoMove3D::on_update(data); + set_center_pos(get_center()); } -bool GLGizmoCut::on_is_activable() const +BoundingBoxf3 GLGizmoCenterMove::bounding_box() const { + BoundingBoxf3 ret; const Selection& selection = m_parent.get_selection(); - return selection.is_single_full_instance() && !selection.is_wipe_tower(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int i : idxs) { + const GLVolume* volume = selection.get_volume(i); + if (!volume->is_modifier) + ret.merge(volume->transformed_convex_hull_bounding_box()); + } + return ret; } -void GLGizmoCut::on_start_dragging() -{ - if (m_hover_id == -1) - return; - const BoundingBoxf3 box = bounding_box(); - m_max_z = box.max.z(); - m_start_z = m_cut_z; - m_drag_pos = m_grabbers[m_hover_id].center; - m_drag_center = box.center(); - m_drag_center.z() = m_cut_z; + +GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) + , m_rotation_gizmo(GLGizmoRotate3D(parent, "", -1)) + , m_move_gizmo(GLGizmoCenterMove(parent, "", -1)) +{ + m_move_gizmo.set_group_id(3); + + m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") +// , _u8L("Radial"), _u8L("Modular") + }; + + m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; + m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; + + m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") +// , _u8L("Claw") + }; + + m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") +// , _u8L("D-shape") + }; + + m_axis_names = { "X", "Y", "Z" }; } -void GLGizmoCut::on_update(const UpdateData& data) +std::string GLGizmoCut3D::get_tooltip() const { - if (m_hover_id != -1) - set_cut_z(m_start_z + calc_projection(data.mouse_ray)); + std::string tooltip = m_rotation_gizmo.get_tooltip(); + if (tooltip.empty()) + tooltip = m_move_gizmo.get_tooltip(); + return tooltip; } -void GLGizmoCut::on_render() +void GLGizmoCut3D::shift_cut_z(double delta) { - const BoundingBoxf3 box = bounding_box(); - Vec3d plane_center = box.center(); - plane_center.z() = m_cut_z; - m_max_z = box.max.z(); - set_cut_z(m_cut_z); - - update_contours(); - - //const float min_x = box.min.x() - Margin - plane_center.x(); - //const float max_x = box.max.x() + Margin - plane_center.x(); - //const float min_y = box.min.y() - Margin - plane_center.y(); - //const float max_y = box.max.y() + Margin - plane_center.y(); -// glsafe(::glEnable(GL_DEPTH_TEST)); -// glsafe(::glDisable(GL_CULL_FACE)); -// glsafe(::glEnable(GL_BLEND)); -// glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); -// -//#if ENABLE_GLBEGIN_GLEND_REMOVAL -//#endif // ENABLE_GLBEGIN_GLEND_REMOVAL -// -// glsafe(::glEnable(GL_CULL_FACE)); -// glsafe(::glDisable(GL_BLEND)); - -#if ENABLE_GLBEGIN_GLEND_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); - - const bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON; - m_old_z = plane_center.z(); - - if (!m_plane.is_initialized() || z_changed) { - m_plane.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; - init_data.reserve_vertices(4); - init_data.reserve_indices(6); - - // vertices - init_data.add_vertex(Vec3f(min_x, min_y, plane_center.z())); - init_data.add_vertex(Vec3f(max_x, min_y, plane_center.z())); - init_data.add_vertex(Vec3f(max_x, max_y, plane_center.z())); - init_data.add_vertex(Vec3f(min_x, max_y, plane_center.z())); - - // indices - init_data.add_ushort_triangle(0, 1, 2); - init_data.add_ushort_triangle(2, 3, 0); - - m_plane.init_from(std::move(init_data)); - } - - m_plane.render(); -#else - // Draw the cutting plane - ::glBegin(GL_QUADS); - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - ::glVertex3f(min_x, min_y, plane_center.z()); - ::glVertex3f(max_x, min_y, plane_center.z()); - ::glVertex3f(max_x, max_y, plane_center.z()); - ::glVertex3f(min_x, max_y, plane_center.z()); - glsafe(::glEnd()); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - - glsafe(::glEnable(GL_CULL_FACE)); - glsafe(::glDisable(GL_BLEND)); - - // Draw the grabber and the connecting line - m_grabbers[0].center = plane_center; - m_grabbers[0].center.z() = plane_center.z() + Offset; - - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - - glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); -#if ENABLE_GLBEGIN_GLEND_REMOVAL - if (!m_grabber_connection.is_initialized() || z_changed) { - m_grabber_connection.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; - init_data.color = ColorRGBA::YELLOW(); - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex((Vec3f)plane_center.cast()); - init_data.add_vertex((Vec3f)m_grabbers[0].center.cast()); - - // indices - init_data.add_ushort_line(0, 1); - - m_grabber_connection.init_from(std::move(init_data)); - } - - m_grabber_connection.render(); - - shader->stop_using(); - } - - shader = wxGetApp().get_shader("gouraud_light"); -#else - glsafe(::glColor3f(1.0, 1.0, 0.0)); - ::glBegin(GL_LINES); - ::glVertex3dv(plane_center.data()); - ::glVertex3dv(m_grabbers[0].center.data()); - glsafe(::glEnd()); - - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - if (shader != nullptr) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - - m_grabbers[0].color = GRABBER_COLOR; - m_grabbers[0].render(m_hover_id == 0, float((box.size().x() + box.size().y() + box.size().z()) / 3.0)); - - shader->stop_using(); - } - -#if ENABLE_GLBEGIN_GLEND_REMOVAL - shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); -#if ENABLE_GLBEGIN_GLEND_REMOVAL - shader->stop_using(); - } -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - } - - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); -// glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); - glsafe(::glRotated(Geometry::rad2deg(m_angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(m_angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(m_angles.x()), 1.0, 0.0, 0.0)); - - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); + Vec3d new_cut_center = m_move_gizmo.get_center(); + new_cut_center[Z] += delta; + set_center(new_cut_center); } -void GLGizmoCut::on_render_for_picking() +void GLGizmoCut3D::set_center(const Vec3d& center) { - glsafe(::glDisable(GL_DEPTH_TEST)); - render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); -} - -void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) -{ - static float last_y = 0.0f; - static float last_h = 0.0f; - - m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - const bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - - // adjust window position to avoid overlap the view toolbar - const float win_h = ImGui::GetWindowHeight(); - y = std::min(y, bottom_limit - win_h); - ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - if (last_h != win_h || last_y != y) { - // ask canvas for another frame to render the window in the correct position - m_imgui->set_requires_extra_frame(); - if (last_h != win_h) - last_h = win_h; - if (last_y != y) - last_y = y; - } - - ImGui::AlignTextToFramePadding(); - m_imgui->text("Z"); - ImGui::SameLine(); - ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f); - - double cut_z = m_cut_z; - if (imperial_units) - cut_z *= ObjectManipulation::mm_to_in; - ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); - - ImGui::SameLine(); - m_imgui->text(imperial_units ? _L("in") : _L("mm")); - - m_cut_z = cut_z * (imperial_units ? ObjectManipulation::in_to_mm : 1.0); - - ImGui::Separator(); - - 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); - - ImGui::Separator(); - - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || m_cut_z <= 0.0 || m_max_z <= m_cut_z); - const bool cut_clicked = m_imgui->button(_L("Perform cut")); - m_imgui->disabled_end(); - - m_imgui->end(); - - if (cut_clicked && (m_keep_upper || m_keep_lower)) - perform_cut(m_parent.get_selection()); + m_move_gizmo.set_center_pos(center); + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); } void GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) @@ -357,17 +195,19 @@ void GLGizmoCut3D::render_move_center_input(int axis) ImGui::SameLine(); ImGui::PushItemWidth(0.3*m_control_width); - double value = axis == Z ? m_cut_plane_gizmo.get_cut_z() : 0.0; + Vec3d move = m_move_gizmo.get_center(); + double in_val, value = in_val = move[axis]; if (m_imperial_units) value *= ObjectManipulation::mm_to_in; ImGui::InputDouble(("##move_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - if (axis == Z) { - double val = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); - m_cut_plane_gizmo.set_cut_z(val); - set_center_z(val); - } + double val = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); + + if (in_val != val) { + move[axis] = val; + set_center(move); + } } void GLGizmoCut3D::render_rotation_input(int axis) @@ -375,8 +215,8 @@ void GLGizmoCut3D::render_rotation_input(int axis) m_imgui->text(m_axis_names[axis] + ":"); ImGui::SameLine(); - Vec3d rotation = get_rotation(); - double value = rotation[axis]; + Vec3d rotation = m_rotation_gizmo.get_rotation(); + double value = rotation[axis] * (180. / M_PI); if (value > 360) value -= 360; @@ -384,9 +224,8 @@ void GLGizmoCut3D::render_rotation_input(int axis) ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - rotation[axis] = value; - set_rotation(rotation); - m_cut_plane_gizmo.set_angles(rotation); + rotation[axis] = (M_PI / 180.) * value; + m_rotation_gizmo.set_rotation(rotation); } void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type) @@ -407,14 +246,13 @@ void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode) void GLGizmoCut3D::render_cut_plane() { - const BoundingBoxf3 box = m_cut_plane_gizmo.bounding_box(); - Vec3d plane_center = box.center(); - plane_center.z() = m_cut_plane_gizmo.get_cut_z(); + const BoundingBoxf3 box = m_move_gizmo.bounding_box(); + Vec3d plane_center = m_move_gizmo.get_center();// == Vec3d::Zero() ? box.center() : m_move_gizmo.get_center(); - const float min_x = box.min.x() - GLGizmoCut::Margin - plane_center.x(); - const float max_x = box.max.x() + GLGizmoCut::Margin - plane_center.x(); - const float min_y = box.min.y() - GLGizmoCut::Margin - plane_center.y(); - const float max_y = box.max.y() + GLGizmoCut::Margin - plane_center.y(); + const float min_x = box.min.x() - GLGizmoCenterMove::Margin - plane_center.x(); + const float max_x = box.max.x() + GLGizmoCenterMove::Margin - plane_center.x(); + const float min_y = box.min.y() - GLGizmoCenterMove::Margin - plane_center.y(); + const float max_y = box.max.y() + GLGizmoCenterMove::Margin - plane_center.y(); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); @@ -426,10 +264,7 @@ void GLGizmoCut3D::render_cut_plane() return; shader->start_using(); -// bool z_changed = std::abs(plane_center.z() - m_old_z) > EPSILON; -// m_old_z = plane_center.z(); - - Vec3d angles = get_rotation(); + Vec3d angles = m_rotation_gizmo.get_rotation(); glsafe(::glPushMatrix()); glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); @@ -437,34 +272,26 @@ void GLGizmoCut3D::render_cut_plane() glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - if (!m_plane.is_initialized()/* || z_changed*/) { + if (!m_plane.is_initialized()) { m_plane.reset(); - GLModel::InitializationData init_data; - GLModel::InitializationData::Entity entity; - entity.type = GLModel::PrimitiveType::Triangles; - entity.positions.reserve(4); - entity.positions.emplace_back(Vec3f(min_x, min_y, 0.0)); - entity.positions.emplace_back(Vec3f(max_x, min_y, 0.0)); - entity.positions.emplace_back(Vec3f(max_x, max_y, 0.0)); - entity.positions.emplace_back(Vec3f(min_x, max_y, 0.0)); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; + init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); - entity.normals.reserve(4); - for (size_t i = 0; i < 4; ++i) { - entity.normals.emplace_back(Vec3f::UnitZ()); - } + // vertices + init_data.add_vertex(Vec3f(min_x, min_y, 0.0)); + init_data.add_vertex(Vec3f(max_x, min_y, 0.0)); + init_data.add_vertex(Vec3f(max_x, max_y, 0.0)); + init_data.add_vertex(Vec3f(min_x, max_y, 0.0)); - entity.indices.reserve(6); - entity.indices.emplace_back(0); - entity.indices.emplace_back(1); - entity.indices.emplace_back(2); - entity.indices.emplace_back(2); - entity.indices.emplace_back(3); - entity.indices.emplace_back(0); + // indices + init_data.add_ushort_triangle(0, 1, 2); + init_data.add_ushort_triangle(2, 3, 0); - init_data.entities.emplace_back(entity); - m_plane.init_from(init_data); - m_plane.set_color(-1, PLANE_COLOR); + m_plane.init_from(std::move(init_data)); } m_plane.render(); @@ -486,159 +313,11 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } -void GLGizmoCut::set_cut_z(double cut_z) -{ - // Clamp the plane to the object's bounding box - m_cut_z = std::clamp(cut_z, 0.0, m_max_z); -} - -void GLGizmoCut::perform_cut(const Selection& selection) -{ - const int instance_idx = selection.get_instance_idx(); - const int object_idx = selection.get_object_idx(); - - wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); - - // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const double object_cut_z = m_cut_z - first_glvolume->get_sla_shift_z(); - - if (0.0 < object_cut_z && object_cut_z < m_max_z) - wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z, - only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | - only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | - only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); - else { - // the object is SLA-elevated and the plane is under it. - } -} - -double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const -{ - double projection = 0.0; - - const Vec3d starting_vec = m_drag_pos - m_drag_center; - const double len_starting_vec = starting_vec.norm(); - if (len_starting_vec != 0.0) { - const Vec3d mouse_dir = mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - const Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - const Vec3d inters_vec = inters - m_drag_pos; - - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec.normalized()); - } - return projection; -} - -BoundingBoxf3 GLGizmoCut::bounding_box() const -{ - BoundingBoxf3 ret; - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int i : idxs) { - const GLVolume* volume = selection.get_volume(i); - if (!volume->is_modifier) - ret.merge(volume->transformed_convex_hull_bounding_box()); - } - return ret; -} - -void GLGizmoCut::update_contours() -{ - const Selection& selection = m_parent.get_selection(); - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box(); - - const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; - const int instance_idx = selection.get_instance_idx(); - std::vector volumes_idxs = std::vector(model_object->volumes.size()); - for (size_t i = 0; i < model_object->volumes.size(); ++i) { - volumes_idxs[i] = model_object->volumes[i]->id(); - } - - if (0.0 < m_cut_z && m_cut_z < m_max_z) { - if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || - m_cut_contours.instance_idx != instance_idx || m_cut_contours.volumes_idxs != volumes_idxs) { - m_cut_contours.cut_z = m_cut_z; - - if (m_cut_contours.object_id != model_object->id() || m_cut_contours.volumes_idxs != volumes_idxs) - m_cut_contours.mesh = model_object->raw_mesh(); - - m_cut_contours.position = Vec3d::Zero();//box.center(); - m_cut_contours.shift = box.center();//Vec3d::Zero(); - m_cut_contours.object_id = model_object->id(); - m_cut_contours.instance_idx = instance_idx; - m_cut_contours.volumes_idxs = volumes_idxs; - m_cut_contours.contours.reset(); - - // addition back transformation - Geometry::Transformation m_transformation = Geometry::Transformation(); - m_transformation.set_offset(-first_glvolume->get_instance_transformation().get_offset()); - m_transformation.set_rotation(-m_angles); - auto cut_params = m_transformation.get_matrix(); - - - MeshSlicingParams slicing_params; - slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix() * cut_params; - - auto cut_z = m_cut_z - box.center().z(); - - const Polygons polys = slice_mesh(m_cut_contours.mesh.its, /*m_*/cut_z, slicing_params); - if (!polys.empty()) { - m_cut_contours.contours.init_from(polys, static_cast(/*m_*/cut_z)); -#if ENABLE_GLBEGIN_GLEND_REMOVAL - m_cut_contours.contours.set_color(ColorRGBA::WHITE()); -#else - m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - } - } - else if (box.center() != m_cut_contours.shift) { - m_cut_contours.shift = box.center();// -m_cut_contours.position; - } - //else if (box.center() != m_cut_contours.position) { - // m_cut_contours.shift = -box.center();// -m_cut_contours.position; - //} - } - else - m_cut_contours.contours.reset(); -} - -GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoRotate3D(parent, icon_filename, sprite_id) - , m_cut_plane_gizmo(GLGizmoCut(parent, "", -1)) -{ - m_cut_plane_gizmo.set_group_id(3); - - m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") -// , _u8L("Radial"), _u8L("Modular") - }; - - m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; - m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; - - m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") -// , _u8L("Claw") - }; - - m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") -// , _u8L("D-shape") - }; - - m_axis_names = {"X", "Y", "Z"}; -} - bool GLGizmoCut3D::on_init() { - if(!GLGizmoRotate3D::on_init()) - return false; - - if (!m_cut_plane_gizmo.init()) + if(!m_rotation_gizmo.init()) + return false; + if(!m_move_gizmo.init()) return false; m_shortcut_key = WXK_CONTROL_C; @@ -650,33 +329,66 @@ std::string GLGizmoCut3D::on_get_name() const return _u8L("Cut"); } +void GLGizmoCut3D::on_set_state() +{ + m_move_gizmo.set_state(m_state); + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + m_rotation_gizmo.set_state(m_state); +} + +void GLGizmoCut3D::on_set_hover_id() +{ + int move_group_id = m_move_gizmo.get_group_id(); + m_rotation_gizmo. set_hover_id((m_hover_id < move_group_id) ? m_hover_id : -1); + m_move_gizmo. set_hover_id((m_hover_id >= move_group_id) ? m_hover_id - move_group_id : -1); +} + +void GLGizmoCut3D::on_enable_grabber(unsigned int id) +{ + m_rotation_gizmo.enable_grabber(id); + m_move_gizmo.enable_grabber(id- m_move_gizmo.get_group_id()); +} + +void GLGizmoCut3D::on_disable_grabber(unsigned int id) +{ + m_rotation_gizmo.disable_grabber(id); + m_move_gizmo.disable_grabber(id- m_move_gizmo.get_group_id()); +} + bool GLGizmoCut3D::on_is_activable() const { - return m_cut_plane_gizmo.is_activable(); + return m_move_gizmo.is_activable(); } void GLGizmoCut3D::on_start_dragging() { - GLGizmoRotate3D::on_start_dragging(); - if (m_hover_id == 3) - m_cut_plane_gizmo.start_dragging(); + m_rotation_gizmo.start_dragging(); + m_move_gizmo.start_dragging(); } void GLGizmoCut3D::on_stop_dragging() { - GLGizmoRotate3D::on_stop_dragging(); - if (m_hover_id == 3) - m_cut_plane_gizmo.stop_dragging(); + m_rotation_gizmo.stop_dragging(); + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + m_move_gizmo.stop_dragging(); +} + +void GLGizmoCut3D::on_update(const UpdateData& data) +{ + m_move_gizmo.update(data); + m_rotation_gizmo.update(data); } void GLGizmoCut3D::on_render() { render_cut_plane(); if (m_mode == CutMode::cutPlanar) { - GLGizmoRotate3D::on_render(); -// if (m_hover_id == -1 || m_hover_id == 3) + int move_group_id = m_move_gizmo.get_group_id(); + if (m_hover_id < move_group_id) + m_rotation_gizmo.render(); + if (m_hover_id == -1 || m_hover_id >= move_group_id) + m_move_gizmo.render(); } - m_cut_plane_gizmo.render(); } void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) @@ -773,7 +485,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) /*|| m_cut_z <= 0.0 || m_max_z <= m_cut_z*/); + m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut()); const bool cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); @@ -783,6 +495,11 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) perform_cut(m_parent.get_selection()); } +bool GLGizmoCut3D::can_perform_cut() const +{ + return true; +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { const int instance_idx = selection.get_instance_idx(); @@ -792,9 +509,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // m_cut_z is the distance from the bed. Subtract possible SLA elevation. const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const double object_cut_z = m_cut_plane_gizmo.get_cut_z() - first_glvolume->get_sla_shift_z(); + const double object_cut_z = m_move_gizmo.get_center().z() - first_glvolume->get_sla_shift_z(); - if (0.0 < object_cut_z/* && object_cut_z < m_max_z*/) + if (0.0 < object_cut_z && can_perform_cut()) wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z, only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f8d7570a4..d1461404b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -3,6 +3,7 @@ #include "GLGizmoBase.hpp" #include "GLGizmoRotate.hpp" +#include "GLGizmoMove.hpp" #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/ObjectID.hpp" @@ -10,75 +11,33 @@ namespace Slic3r { namespace GUI { -class GLGizmoCut : public GLGizmoBase +class GLGizmoCenterMove : public GLGizmoMove3D { public: - static const double Offset; static const double Margin; private: - double m_cut_z{ 0.0 }; - double m_max_z{ 0.0 }; - double m_start_z{ 0.0 }; - Vec3d m_drag_pos; - Vec3d m_drag_center; - bool m_keep_upper{ true }; - bool m_keep_lower{ true }; - bool m_rotate_lower{ false }; -#if ENABLE_GLBEGIN_GLEND_REMOVAL - GLModel m_grabber_connection; - float m_old_z{ 0.0f }; -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - Vec3d m_angles{ Vec3d::Zero() }; - - struct CutContours - { - TriangleMesh mesh; - GLModel contours; - double cut_z{ 0.0 }; - Vec3d position{ Vec3d::Zero() }; - Vec3d shift{ Vec3d::Zero() }; - ObjectID object_id; - int instance_idx{ -1 }; - std::vector volumes_idxs; - }; - - CutContours m_cut_contours; + Vec3d m_min_pos{ Vec3d::Zero() }; + Vec3d m_max_pos{ Vec3d::Zero() }; public: - GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - - double get_cut_z() const { return m_cut_z; } - void set_cut_z(double cut_z); - void set_angles(const Vec3d& angles) { m_angles = angles; } - + GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; protected: - virtual bool on_init() override; - virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual std::string on_get_name() const override; virtual void on_set_state() override; - virtual bool on_is_activable() const override; - virtual void on_start_dragging() override; virtual void on_update(const UpdateData& data) override; - virtual void on_render() override; - virtual void on_render_for_picking() override; - virtual void on_render_input_window(float x, float y, float bottom_limit) override; - -private: - void perform_cut(const Selection& selection); - double calc_projection(const Linef3& mouse_ray) const; public: - BoundingBoxf3 bounding_box() const; - void update_contours(); + void set_center_pos(const Vec3d& center_pos); + BoundingBoxf3 bounding_box() const; }; -class GLGizmoCut3D : public GLGizmoRotate3D + +class GLGizmoCut3D : public GLGizmoBase { - GLGizmoCut m_cut_plane_gizmo; + GLGizmoRotate3D m_rotation_gizmo; + GLGizmoCenterMove m_move_gizmo; #if ENABLE_GLBEGIN_GLEND_REMOVAL GLModel m_plane; @@ -148,57 +107,37 @@ class GLGizmoCut3D : public GLGizmoRotate3D public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - std::string get_tooltip() const override { - std::string tooltip = GLGizmoRotate3D::get_tooltip(); - if (tooltip.empty()) - tooltip = m_cut_plane_gizmo.get_tooltip(); - return tooltip; - } + std::string get_tooltip() const override; + + void shift_cut_z(double delta); protected: bool on_init() override; std::string on_get_name() const override; - void on_set_state() override { - GLGizmoRotate3D::on_set_state(); - m_cut_plane_gizmo.set_state(m_state); - } - void on_set_hover_id() override { - GLGizmoRotate3D::on_set_hover_id(); - m_cut_plane_gizmo.set_hover_id((m_hover_id == 3) ? 0 : -1); - } - void on_enable_grabber(unsigned int id) override { - GLGizmoRotate3D::on_enable_grabber(id); - if (id == 3) - m_cut_plane_gizmo.enable_grabber(0); - } - void on_disable_grabber(unsigned int id) override { - GLGizmoRotate3D::on_disable_grabber(id); - if (id == 3) - m_cut_plane_gizmo.disable_grabber(0); - } + void on_set_state() override; + void on_set_hover_id() override; + void on_enable_grabber(unsigned int id) override; + void on_disable_grabber(unsigned int id) override; bool on_is_activable() const override; void on_start_dragging() override; void on_stop_dragging() override; - void on_update(const UpdateData& data) override { - GLGizmoRotate3D::on_update(data); - m_cut_plane_gizmo.update(data); - } + void on_update(const UpdateData& data) override; void on_render() override; void on_render_for_picking() override { - GLGizmoRotate3D::on_render_for_picking(); - m_cut_plane_gizmo.render_for_picking(); + m_rotation_gizmo.render_for_picking(); + m_move_gizmo.render_for_picking(); } - void on_render_input_window(float x, float y, float bottom_limit) override; private: - + void set_center(const Vec3d& center); void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); void render_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(ConnectorMode mode); void render_connect_type_radio_button(ConnectorType type); + bool can_perform_cut() const; void render_cut_plane(); void perform_cut(const Selection& selection); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index ff921ea7c..e12974b75 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -78,6 +78,9 @@ void GLGizmoMove3D::on_update(const UpdateData& data) m_displacement.y() = calc_projection(data); else if (m_hover_id == 2) m_displacement.z() = calc_projection(data); + + if (m_has_forced_center) + m_center += m_displacement; } void GLGizmoMove3D::on_render() @@ -91,19 +94,27 @@ void GLGizmoMove3D::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const BoundingBoxf3& box = selection.get_bounding_box(); - const Vec3d& center = box.center(); + const Vec3d& center = m_has_forced_center ? m_center : box.center(); - // x axis - m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; - m_grabbers[0].color = AXES_COLOR[0]; + if (m_has_forced_center) + for (auto axis : { X, Y, Z }) { + m_grabbers[axis].center = center; + m_grabbers[axis].center[axis] += 0.5*fabs(box.max[axis] - box.min[axis]); + m_grabbers[axis].color = AXES_COLOR[axis]; + } + else { + // x axis + m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; + m_grabbers[0].color = AXES_COLOR[0]; - // y axis - m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; - m_grabbers[1].color = AXES_COLOR[1]; + // y axis + m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; + m_grabbers[1].color = AXES_COLOR[1]; - // z axis - m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; - m_grabbers[2].color = AXES_COLOR[2]; + // z axis + m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; + m_grabbers[2].color = AXES_COLOR[2]; + } glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); @@ -216,7 +227,10 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; + const Vec3d& starting_drag_position = m_has_forced_center ? m_grabbers[m_hover_id].center : m_starting_drag_position; + const Vec3d& starting_box_center = m_has_forced_center ? m_center : m_starting_box_center; + + Vec3d starting_vec = starting_drag_position - starting_box_center; double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.mouse_ray.unit_vector(); @@ -224,9 +238,9 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - m_starting_drag_position; + Vec3d inters_vec = inters - starting_drag_position; // finds projection of the vector along the staring direction projection = inters_vec.dot(starting_vec.normalized()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 2a75df866..9e404a95c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -17,6 +17,9 @@ class GLGizmoMove3D : public GLGizmoBase Vec3d m_starting_box_center{ Vec3d::Zero() }; Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; + Vec3d m_center{ Vec3d::Zero() }; + bool m_has_forced_center{ false }; + GLModel m_cone; #if ENABLE_GLBEGIN_GLEND_REMOVAL struct GrabberConnection @@ -38,6 +41,9 @@ public: std::string get_tooltip() const override; + void set_center(Vec3d center) { m_center = center; m_has_forced_center = true; } + const Vec3d& get_center() const { return m_center; } + protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 9869634cc..b61310d6c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -38,9 +38,10 @@ void GLGizmoRotate::set_angle(double angle) m_angle = angle; } -void GLGizmoRotate::set_center_z(double center_z) +void GLGizmoRotate::set_center(const Vec3d& center) { - m_center_z = center_z; + m_forced_center = center; + m_has_forced_center = true; } std::string GLGizmoRotate::get_tooltip() const @@ -64,9 +65,7 @@ bool GLGizmoRotate::on_init() void GLGizmoRotate::on_start_dragging() { const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); - m_center = box.center(); - if (m_center_z >= 0) - m_center[Z] = m_center_z; + m_center = m_has_forced_center ? m_forced_center : box.center(); m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; @@ -118,9 +117,7 @@ void GLGizmoRotate::on_render() const BoundingBoxf3& box = selection.get_bounding_box(); if (m_hover_id != 0 && !m_grabbers.front().dragging) { - m_center = box.center(); - if (m_center_z >= 0) - m_center[Z] = m_center_z; + m_center = m_has_forced_center ? m_forced_center : box.center(); m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 0c291cbef..7eee2069c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -28,13 +28,14 @@ public: private: Axis m_axis; double m_angle{ 0.0 }; - double m_center_z{ -1.0 }; Vec3d m_center{ Vec3d::Zero() }; float m_radius{ 0.0f }; float m_snap_coarse_in_radius{ 0.0f }; float m_snap_coarse_out_radius{ 0.0f }; float m_snap_fine_in_radius{ 0.0f }; float m_snap_fine_out_radius{ 0.0f }; + bool m_has_forced_center{false}; + Vec3d m_forced_center{ Vec3d::Zero() }; GLModel m_cone; #if ENABLE_GLBEGIN_GLEND_REMOVAL @@ -60,6 +61,7 @@ public: double get_angle() const { return m_angle; } void set_angle(double angle); void set_center_z(double center_z); + void set_center(const Vec3d& center); std::string get_tooltip() const override; @@ -103,7 +105,7 @@ public: Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } - void set_center_z(double center_z) { m_gizmos[X].set_center_z(center_z); m_gizmos[Y].set_center_z(center_z); m_gizmos[Z].set_center_z(center_z); } + void set_center(const Vec3d& center) { m_gizmos[X].set_center(center); m_gizmos[Y].set_center(center); m_gizmos[Z].set_center(center); } std::string get_tooltip() const override { std::string tooltip = m_gizmos[X].get_tooltip(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 3a144a65d..ba1419fea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -919,8 +919,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) else if (m_current == Cut) { auto do_move = [this, &processed](double delta_z) { - GLGizmoCut* cut = dynamic_cast(get_current()); - cut->set_cut_z(delta_z + cut->get_cut_z()); + GLGizmoCut3D* cut = dynamic_cast(get_current()); + cut->shift_cut_z(delta_z); processed = true; }; From 389b7ce4bd8968de92cc1c31aa5f6dd4e1cfb03a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 7 Feb 2022 10:09:46 +0100 Subject: [PATCH 04/93] MeshClipper extended: - direction and range of the clipping plane can be now set from the outside - it is now able to show a contour of the cut (not yet ideal with multipart objects that overlap) --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 10 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 33 +++++- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 8 +- src/slic3r/GUI/MeshUtils.cpp | 109 +++++++++++++++--- src/slic3r/GUI/MeshUtils.hpp | 13 ++- 10 files changed, 161 insertions(+), 38 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 66b6dcf60..b6da2e63d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -295,7 +295,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -304,7 +304,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::SameLine(sliders_left_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel"))) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index be52ebcb9..e8a6f6e2c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -383,19 +383,19 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (action == SLAGizmoEventType::MouseWheelUp && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::max(0., pos - 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } @@ -693,7 +693,7 @@ RENDER_AGAIN: else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -702,7 +702,7 @@ RENDER_AGAIN: ImGui::PushItemWidth(window_width - settings_sliders_left); float clp_dist = m_c->object_clipper()->get_position(); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); // make sure supports are shown/hidden as appropriate bool show_sups = m_c->instances_hider()->are_supports_shown(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index dd9cf0de2..77dc3a966 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -463,7 +463,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_imgui->text(m_desc.at("clipping_of_view")); } else { if (m_imgui->button(m_desc.at("reset_direction"))) { - wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position(-1., false); }); + wxGetApp().CallAfter([this]() { m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -471,7 +471,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::SameLine(sliders_left_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel"))) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 82a816d1b..30cf2b31c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -429,7 +429,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous pos = action == SLAGizmoEventType::MouseWheelDown ? std::max(0., pos - 0.01) : std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } else if (alt_down) { @@ -461,7 +461,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 5f6cd7a95..6d49b276c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -157,7 +157,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -166,7 +166,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(sliders_left_width); ImGui::PushItemWidth(window_width - sliders_left_width - slider_icon_width); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true, _L("Ctrl + Mouse wheel"))) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); ImGui::Separator(); if (m_imgui->button(m_desc.at("remove_all"))) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fa0b27269..e1bc32bbe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -516,19 +516,19 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if (action == SLAGizmoEventType::MouseWheelUp && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::min(1., pos + 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { double pos = m_c->object_clipper()->get_position(); pos = std::max(0., pos - 0.01); - m_c->object_clipper()->set_position(pos, true); + m_c->object_clipper()->set_position_by_ratio(pos, true); return true; } if (action == SLAGizmoEventType::ResetClippingPlane) { - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); return true; } @@ -826,7 +826,7 @@ RENDER_AGAIN: else { if (m_imgui->button(m_desc.at("reset_direction"))) { wxGetApp().CallAfter([this](){ - m_c->object_clipper()->set_position(-1., false); + m_c->object_clipper()->set_position_by_ratio(-1., false); }); } } @@ -835,7 +835,7 @@ RENDER_AGAIN: ImGui::PushItemWidth(window_width - clipping_slider_left); float clp_dist = m_c->object_clipper()->get_position(); if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) - m_c->object_clipper()->set_position(clp_dist, true); + m_c->object_clipper()->set_position_by_ratio(clp_dist, true); if (m_imgui->button("?")) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 1e49ebc8c..48a54fbae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -425,18 +425,20 @@ void ObjectClipper::render_cut() const glsafe(::glPushMatrix()); #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL clipper->render_cut({ 1.0f, 0.37f, 0.0f, 1.0f }); + clipper->render_contour({ 1.f, 1.f, 1.f, 1.f}); #else glsafe(::glColor3f(1.0f, 0.37f, 0.0f)); clipper->render_cut(); -#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + glsafe(::glColor3f(1.f, 1.f, 1.f)); + clipper->render_contour(); +#endif glsafe(::glPopMatrix()); ++clipper_id; } } - -void ObjectClipper::set_position(double pos, bool keep_normal) +void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { const ModelObject* mo = get_pool()->selection_info()->model_object(); int active_inst = get_pool()->selection_info()->get_active_instance(); @@ -454,6 +456,28 @@ void ObjectClipper::set_position(double pos, bool keep_normal) get_pool()->get_canvas()->set_as_dirty(); } +void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos) +{ + Vec3d normal = end-origin; + double norm = normal.norm(); + pos = std::clamp(pos, 0.0001, norm); + m_clp.reset(new ClippingPlane(normal, pos)); + m_clp_ratio = pos/norm; + get_pool()->get_canvas()->set_as_dirty(); +} + +const ClippingPlane* ObjectClipper::get_clipping_plane() const +{ + static const ClippingPlane no_clip = ClippingPlane::ClipsNothing(); + return m_hide_clipped ? m_clp.get() : &no_clip; +} + +void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contour_width) +{ + m_hide_clipped = hide_clipped; + for (auto& clipper : m_clippers) + clipper->set_behaviour(fill_cut, contour_width); +} void SupportsClipper::on_update() @@ -542,9 +566,12 @@ void SupportsClipper::render_cut() const glsafe(::glPushMatrix()); #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f }); + m_clipper->render_contour({ 1.f, 1.f, 1.f, 1.f }); #else glsafe(::glColor3f(1.0f, 0.f, 0.37f)); m_clipper->render_cut(); + glsafe(::glColor3f(1.0f, 1.f, 1.f)); + m_clipper->render_contour(); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopMatrix()); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 228f5b58c..c5c238408 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -255,10 +255,13 @@ public: CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; } #endif // NDEBUG - void set_position(double pos, bool keep_normal); + void set_normal(const Vec3d& dir); double get_position() const { return m_clp_ratio; } - ClippingPlane* get_clipping_plane() const { return m_clp.get(); } + const ClippingPlane* get_clipping_plane() const; void render_cut() const; + void set_position_by_ratio(double pos, bool keep_normal); + void set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos); + void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); protected: @@ -271,6 +274,7 @@ private: std::unique_ptr m_clp; double m_clp_ratio = 0.; double m_active_inst_bb_radius = 0.; + bool m_hide_clipped = true; }; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 3787abb2f..ac9437590 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -19,6 +19,16 @@ namespace Slic3r { namespace GUI { +void MeshClipper::set_behaviour(bool fill_cut, double contour_width) +{ + if (fill_cut != m_fill_cut || contour_width != m_contour_width) + m_triangles_valid = false; + m_fill_cut = fill_cut; + m_contour_width = contour_width; +} + + + void MeshClipper::set_plane(const ClippingPlane& plane) { if (m_plane != plane) { @@ -43,7 +53,6 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh) if (m_mesh != &mesh) { m_mesh = &mesh; m_triangles_valid = false; - m_triangles2d.resize(0); } } @@ -52,7 +61,6 @@ void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) if (m_negative_mesh != &mesh) { m_negative_mesh = &mesh; m_triangles_valid = false; - m_triangles2d.resize(0); } } @@ -63,12 +71,10 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo) if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) { m_trafo = trafo; m_triangles_valid = false; - m_triangles2d.resize(0); } } - #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void MeshClipper::render_cut(const ColorRGBA& color) #else @@ -77,7 +83,6 @@ void MeshClipper::render_cut() { if (! m_triangles_valid) recalculate_triangles(); - #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); if (curr_shader != nullptr) @@ -100,6 +105,36 @@ void MeshClipper::render_cut() } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void MeshClipper::render_contour(const ColorRGBA& color) +#else +void MeshClipper::render_contour() +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +{ + if (! m_triangles_valid) + recalculate_triangles(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); + if (curr_shader != nullptr) + curr_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + m_model_expanded.set_color(color); + m_model_expanded.render(); + shader->stop_using(); + } + + if (curr_shader != nullptr) + curr_shader->start_using(); +#else + if (m_vertex_array_expanded.has_VBOs()) + m_vertex_array_expanded.render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +} + + void MeshClipper::recalculate_triangles() { @@ -181,24 +216,25 @@ void MeshClipper::recalculate_triangles() } } - m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.); - tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting + std::vector triangles2d = m_fill_cut + ? triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.) + : std::vector(); #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_model.reset(); GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(m_triangles2d.size()) }; - init_data.reserve_vertices(m_triangles2d.size()); - init_data.reserve_indices(m_triangles2d.size()); + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(triangles2d.size()) }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); // vertices + indices - for (auto it = m_triangles2d.cbegin(); it != m_triangles2d.cend(); it = it + 3) { + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - const size_t idx = it - m_triangles2d.cbegin(); + const size_t idx = it - triangles2d.cbegin(); if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); else @@ -209,16 +245,61 @@ void MeshClipper::recalculate_triangles() m_model.init_from(std::move(init_data)); #else m_vertex_array.release_geometry(); - for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) { + for (auto it=triangles2d.cbegin(); it != triangles2d.cend(); it=it+3) { m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); m_vertex_array.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); m_vertex_array.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); - const size_t idx = it - m_triangles2d.cbegin(); + const size_t idx = it - triangles2d.cbegin(); m_vertex_array.push_triangle(idx, idx+1, idx+2); } m_vertex_array.finalize_geometry(true); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + + triangles2d = {}; + if (m_contour_width != 0.) { + ExPolygons expolys_exp = offset_ex(expolys, scale_(m_contour_width)); + expolys_exp = diff_ex(expolys_exp, expolys); + triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.); + } + + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_model_expanded.reset(); + + init_data = GLModel::Geometry(); + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(triangles2d.size()) }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); + + // vertices + indices + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - triangles2d.cbegin(); + if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + else + init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + } + + if (!init_data.is_empty()) + m_model_expanded.init_from(std::move(init_data)); +#else + m_vertex_array_expanded.release_geometry(); + for (auto it=triangles2d.cbegin(); it != triangles2d.cend(); it=it+3) { + m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); + m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); + m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); + const size_t idx = it - triangles2d.cbegin(); + m_vertex_array_expanded.push_triangle(idx, idx+1, idx+2); + } + m_vertex_array_expanded.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + + + m_triangles_valid = true; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index cc961ee8f..59a7b34ae 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -77,6 +77,10 @@ public: class MeshClipper { public: + // Set whether the cut should be triangulated and whether a cut + // contour should be calculated and shown. + void set_behaviour(bool fill_cut, double contour_width); + // Inform MeshClipper about which plane we want to use to cut the mesh // This is supposed to be in world coordinates. void set_plane(const ClippingPlane& plane); @@ -100,8 +104,12 @@ public: // be set in world coords. #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void render_cut(const ColorRGBA& color); + void render_contour(const ColorRGBA& color); #else void render_cut(); + // Render the triangulated contour. Transformation matrices should + // be set in world coords. + void render_contour(); #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL private: @@ -112,13 +120,16 @@ private: const TriangleMesh* m_negative_mesh = nullptr; ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); - std::vector m_triangles2d; #if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLModel m_model; + GLModel m_model_expanded; #else GLIndexedVertexArray m_vertex_array; + GLIndexedVertexArray m_vertex_array_expanded; #endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL bool m_triangles_valid = false; + bool m_fill_cut = true; + double m_contour_width = 0.; }; From 7fef26527b02a80d604fa774c08e661fc37006ae Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 7 Feb 2022 10:13:15 +0100 Subject: [PATCH 05/93] Cut gizmo uses the common ObjectClipper to show the cut and contour --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 140 ++++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 50 +++++----- 2 files changed, 109 insertions(+), 81 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index ff5d89f5e..e5692cc53 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -88,9 +88,10 @@ void GLGizmoCut::on_render() Vec3d plane_center = box.center(); plane_center.z() = m_cut_z; m_max_z = box.max.z(); - set_cut_z(m_cut_z); + set_cut_z(m_cut_z); // FIXME: We should not call this during each render loop. - update_contours(); + // update_contours(); + m_c->object_clipper()->render_cut(); const float min_x = box.min.x() - Margin; const float max_x = box.max.x() + Margin; @@ -198,20 +199,20 @@ void GLGizmoCut::on_render() shader->stop_using(); } -#if ENABLE_GLBEGIN_GLEND_REMOVAL - shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); - glsafe(::glLineWidth(2.0f)); - m_cut_contours.contours.render(); - glsafe(::glPopMatrix()); -#if ENABLE_GLBEGIN_GLEND_REMOVAL - shader->stop_using(); - } -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL +// #if ENABLE_GLBEGIN_GLEND_REMOVAL +// shader = wxGetApp().get_shader("flat"); +// if (shader != nullptr) { +// shader->start_using(); +// #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +// glsafe(::glPushMatrix()); +// glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z())); +// glsafe(::glLineWidth(2.0f)); +// m_cut_contours.contours.render(); +// glsafe(::glPopMatrix()); +// #if ENABLE_GLBEGIN_GLEND_REMOVAL +// shader->stop_using(); +// } +// #endif // ENABLE_GLBEGIN_GLEND_REMOVAL } void GLGizmoCut::on_render_for_picking() @@ -269,6 +270,16 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) const bool cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); + //////// + static bool hide_clipped = true; + static bool fill_cut = true; + static float contour_width = 0.; + m_imgui->checkbox("hide_clipped", hide_clipped); + m_imgui->checkbox("fill_cut", fill_cut); + m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); + m_c->object_clipper()->set_behavior(hide_clipped, fill_cut, contour_width); + //////// + m_imgui->end(); if (cut_clicked && (m_keep_upper || m_keep_lower)) @@ -279,6 +290,12 @@ void GLGizmoCut::set_cut_z(double cut_z) { // Clamp the plane to the object's bounding box m_cut_z = std::clamp(cut_z, 0.0, m_max_z); + + const BoundingBoxf3 box = bounding_box(); + Vec3d plane_center = box.center(); + plane_center.z() = 0; + m_c->object_clipper()->set_range_and_pos(plane_center, + plane_center + m_max_z * Vec3d::UnitZ(), m_cut_z); } void GLGizmoCut::perform_cut(const Selection& selection) @@ -337,52 +354,61 @@ BoundingBoxf3 GLGizmoCut::bounding_box() const return ret; } -void GLGizmoCut::update_contours() -{ - const Selection& selection = m_parent.get_selection(); - const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box(); +// void GLGizmoCut::update_contours() +// { +// const Selection& selection = m_parent.get_selection(); +// const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); +// const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box(); - const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; - const int instance_idx = selection.get_instance_idx(); - std::vector volumes_idxs = std::vector(model_object->volumes.size()); - for (size_t i = 0; i < model_object->volumes.size(); ++i) { - volumes_idxs[i] = model_object->volumes[i]->id(); - } +// const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()]; +// const int instance_idx = selection.get_instance_idx(); +// std::vector volumes_idxs = std::vector(model_object->volumes.size()); +// for (size_t i = 0; i < model_object->volumes.size(); ++i) { +// volumes_idxs[i] = model_object->volumes[i]->id(); +// } - if (0.0 < m_cut_z && m_cut_z < m_max_z) { - if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || - m_cut_contours.instance_idx != instance_idx || m_cut_contours.volumes_idxs != volumes_idxs) { - m_cut_contours.cut_z = m_cut_z; +// if (0.0 < m_cut_z && m_cut_z < m_max_z) { +// if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_id != model_object->id() || +// m_cut_contours.instance_idx != instance_idx || m_cut_contours.volumes_idxs != volumes_idxs) { +// m_cut_contours.cut_z = m_cut_z; - if (m_cut_contours.object_id != model_object->id() || m_cut_contours.volumes_idxs != volumes_idxs) - m_cut_contours.mesh = model_object->raw_mesh(); +// if (m_cut_contours.object_id != model_object->id() || m_cut_contours.volumes_idxs != volumes_idxs) +// m_cut_contours.mesh = model_object->raw_mesh(); - m_cut_contours.position = box.center(); - m_cut_contours.shift = Vec3d::Zero(); - m_cut_contours.object_id = model_object->id(); - m_cut_contours.instance_idx = instance_idx; - m_cut_contours.volumes_idxs = volumes_idxs; - m_cut_contours.contours.reset(); +// m_cut_contours.position = box.center(); +// m_cut_contours.shift = Vec3d::Zero(); +// m_cut_contours.object_id = model_object->id(); +// m_cut_contours.instance_idx = instance_idx; +// m_cut_contours.volumes_idxs = volumes_idxs; +// m_cut_contours.contours.reset(); - MeshSlicingParams slicing_params; - slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix(); - const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params); - if (!polys.empty()) { - m_cut_contours.contours.init_from(polys, static_cast(m_cut_z)); -#if ENABLE_GLBEGIN_GLEND_REMOVAL - m_cut_contours.contours.set_color(ColorRGBA::WHITE()); -#else - m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - } - } - else if (box.center() != m_cut_contours.position) { - m_cut_contours.shift = box.center() - m_cut_contours.position; - } - } - else - m_cut_contours.contours.reset(); +// MeshSlicingParams slicing_params; +// slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix(); +// const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params); +// if (!polys.empty()) { +// m_cut_contours.contours.init_from(polys, static_cast(m_cut_z)); +// #if ENABLE_GLBEGIN_GLEND_REMOVAL +// m_cut_contours.contours.set_color(ColorRGBA::WHITE()); +// #else +// m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); +// #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +// } +// } +// else if (box.center() != m_cut_contours.position) { +// m_cut_contours.shift = box.center() - m_cut_contours.position; +// } +// } +// else +// m_cut_contours.contours.reset(); +// } + + + +CommonGizmosDataID GLGizmoCut::on_get_requirements() const { + return CommonGizmosDataID( + int(CommonGizmosDataID::SelectionInfo) + | int(CommonGizmosDataID::InstancesHider) + | int(CommonGizmosDataID::ObjectClipper)); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ccf8732cf..26bf7c56f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -28,19 +28,19 @@ class GLGizmoCut : public GLGizmoBase float m_old_z{ 0.0f }; #endif // ENABLE_GLBEGIN_GLEND_REMOVAL - struct CutContours - { - TriangleMesh mesh; - GLModel contours; - double cut_z{ 0.0 }; - Vec3d position{ Vec3d::Zero() }; - Vec3d shift{ Vec3d::Zero() }; - ObjectID object_id; - int instance_idx{ -1 }; - std::vector volumes_idxs; - }; + // struct CutContours + // { + // TriangleMesh mesh; + // GLModel contours; + // double cut_z{ 0.0 }; + // Vec3d position{ Vec3d::Zero() }; + // Vec3d shift{ Vec3d::Zero() }; + // ObjectID object_id; + // int instance_idx{ -1 }; + // std::vector volumes_idxs; + // }; - CutContours m_cut_contours; + // CutContours m_cut_contours; public: GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -51,23 +51,25 @@ public: std::string get_tooltip() const override; protected: - virtual bool on_init() override; - virtual void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - virtual std::string on_get_name() const override; - virtual void on_set_state() override; - virtual bool on_is_activable() const override; - virtual void on_start_dragging() override; - virtual void on_update(const UpdateData& data) override; - virtual void on_render() override; - virtual void on_render_for_picking() override; - virtual void on_render_input_window(float x, float y, float bottom_limit) override; + bool on_init() override; + void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } + std::string on_get_name() const override; + void on_set_state() override; + bool on_is_activable() const override; + void on_start_dragging() override; + void on_update(const UpdateData& data) override; + void on_render() override; + void on_render_for_picking() override; + void on_render_input_window(float x, float y, float bottom_limit) override; + CommonGizmosDataID on_get_requirements() const override; + private: void perform_cut(const Selection& selection); double calc_projection(const Linef3& mouse_ray) const; BoundingBoxf3 bounding_box() const; - void update_contours(); + //void update_contours(); }; } // namespace GUI From 016a7feb3d95cedc0006d369ba16a331538f7a65 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 7 Feb 2022 14:34:21 +0100 Subject: [PATCH 06/93] Extender MeshRaycaster so it can also provide hits on the clipping plane --- src/slic3r/GUI/MeshUtils.cpp | 28 ++++++++++++++++++++++++---- src/slic3r/GUI/MeshUtils.hpp | 5 +++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index ac9437590..40b1dc3bc 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -334,8 +334,11 @@ void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3 bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane, - size_t* facet_idx) const + size_t* facet_idx, bool* was_clipping_plane_hit) const { + if (was_clipping_plane_hit) + *was_clipping_plane_hit = false; + Vec3d point; Vec3d direction; line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); @@ -356,9 +359,26 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& break; } - if (i==hits.size() || (hits.size()-i) % 2 != 0) { - // All hits are either clipped, or there is an odd number of unclipped - // hits - meaning the nearest must be from inside the mesh. + if (i==hits.size()) { + // All hits are clipped. + return false; + } + if ((hits.size()-i) % 2 != 0) { + // There is an odd number of unclipped hits - meaning the nearest must be from inside the mesh. + // In that case, calculate intersection with the clipping place. + if (clipping_plane && was_clipping_plane_hit) { + direction = direction + point; + point = trafo * point; // transform to world coords + direction = trafo * direction - point; + + Vec3d normal = -clipping_plane->get_normal().cast(); + double den = normal.dot(direction); + if (den != 0.) { + double t = (-clipping_plane->get_offset() - normal.dot(point))/den; + position = (point + t * direction).cast(); + *was_clipping_plane_hit = true; + } + } return false; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 59a7b34ae..f5d031d92 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -154,10 +154,11 @@ public: const Vec2d& mouse_pos, const Transform3d& trafo, // how to get the mesh into world coords const Camera& camera, // current camera position - Vec3f& position, // where to save the positibon of the hit (mesh coords) + Vec3f& position, // where to save the positibon of the hit (mesh coords if mesh, world coords if clipping plane) Vec3f& normal, // normal of the triangle that was hit const ClippingPlane* clipping_plane = nullptr, // clipping plane (if active) - size_t* facet_idx = nullptr // index of the facet hit + size_t* facet_idx = nullptr, // index of the facet hit + bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section? ) const; // Given a vector of points in woorld coordinates, this returns vector From a8564bf289224f833ba3a129f29c2fe8e1d7f860 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 7 Feb 2022 14:35:34 +0100 Subject: [PATCH 07/93] Cut gizmo is now able to see clicks on the clipping plane --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 34 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 ++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 ++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e5692cc53..fc7fe0fe9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -403,12 +403,44 @@ BoundingBoxf3 GLGizmoCut::bounding_box() const // } +bool GLGizmoCut::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (is_dragging()) + return false; + const ModelObject *mo = m_c->selection_info()->model_object(); + const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Transform3d instance_trafo = mi->get_transformation().get_matrix(); + const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); + const Camera& camera = wxGetApp().plater()->get_camera(); + + int mesh_id = -1; + for (const ModelVolume *mv : mo->volumes) { + ++mesh_id; + if (! mv->is_model_part()) + continue; + Vec3f hit; + Vec3f normal; + bool clipping_plane_was_hit = false; + m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), + camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + nullptr, &clipping_plane_was_hit); + if (clipping_plane_was_hit) { + // The clipping plane was clicked, hit containts coordinates of the hit in world coords. + std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; + return true; + } + } + return false; +} + + CommonGizmosDataID GLGizmoCut::on_get_requirements() const { return CommonGizmosDataID( int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) - | int(CommonGizmosDataID::ObjectClipper)); + | int(CommonGizmosDataID::ObjectClipper) + | int(CommonGizmosDataID::Raycaster)); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 26bf7c56f..a084f346c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -9,6 +9,8 @@ namespace Slic3r { namespace GUI { +enum class SLAGizmoEventType : unsigned char; + class GLGizmoCut : public GLGizmoBase { static const double Offset; @@ -49,6 +51,7 @@ public: void set_cut_z(double cut_z); std::string get_tooltip() const override; + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); protected: bool on_init() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 55cbb0c30..6d122a419 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -445,6 +445,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == MmuSegmentation) return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + else if (m_current == Cut) + return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } @@ -656,7 +658,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) m_tooltip.clear(); if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) + if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; From af03bed09484369ff9d725f72ed22fb0f01af21b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 17 Feb 2022 14:32:08 +0100 Subject: [PATCH 08/93] Cut: Implemented update_clipper() --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 44 +++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 8 ++--- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 6 ++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 58484c8a5..208549bc4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -126,6 +126,35 @@ void GLGizmoCut3D::shift_cut_z(double delta) set_center(new_cut_center); } +void GLGizmoCut3D::update_clipper() +{ + const Vec3d& angles = m_rotation_gizmo.get_rotation(); + Matrix3d m; + m = Eigen::AngleAxisd(angles[X], Vec3d::UnitX()) + * Eigen::AngleAxisd(angles[Y], Vec3d::UnitY()) + * Eigen::AngleAxisd(angles[Z], Vec3d::UnitZ()); + + Vec3d plane_center = m_move_gizmo.get_center(); + BoundingBoxf3 box = m_move_gizmo.bounding_box(); + + Vec3d min, max = min = plane_center = m_move_gizmo.get_center(); + min[Z] = box.min.z(); + max[Z] = box.max.z(); + + min -= plane_center; + max -= plane_center; + + Vec3d beg = m * min; + Vec3d end = m * max; + + beg += plane_center; + end += plane_center; + + double dist = (plane_center - beg).norm(); + + m_c->object_clipper()->set_range_and_pos(beg, end, dist); +} + void GLGizmoCut3D::set_center(const Vec3d& center) { m_move_gizmo.set_center_pos(center); @@ -216,7 +245,7 @@ void GLGizmoCut3D::render_rotation_input(int axis) ImGui::SameLine(); Vec3d rotation = m_rotation_gizmo.get_rotation(); - double value = rotation[axis] * (180. / M_PI); + double value = Geometry::rad2deg(rotation[axis]); if (value > 360) value -= 360; @@ -224,7 +253,7 @@ void GLGizmoCut3D::render_rotation_input(int axis) ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - rotation[axis] = (M_PI / 180.) * value; + rotation[axis] = Geometry::deg2rad(value); m_rotation_gizmo.set_rotation(rotation); } @@ -248,7 +277,7 @@ void GLGizmoCut3D::render_cut_plane() { const BoundingBoxf3 box = m_move_gizmo.bounding_box(); Vec3d plane_center = m_move_gizmo.get_center();// == Vec3d::Zero() ? box.center() : m_move_gizmo.get_center(); - // update_contours(); + m_c->object_clipper()->render_cut(); const float min_x = box.min.x() - GLGizmoCenterMove::Margin - plane_center.x(); @@ -383,6 +412,7 @@ void GLGizmoCut3D::on_update(const UpdateData& data) void GLGizmoCut3D::on_render() { + update_clipper(); render_cut_plane(); if (m_mode == CutMode::cutPlanar) { int move_group_id = m_move_gizmo.get_group_id(); @@ -435,7 +465,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(m_label_width); for (Axis axis : {X, Y, Z}) render_rotation_input(axis); - m_imgui->text(_L("°")); + m_imgui->text(_L("°")); } else { ImGui::AlignTextToFramePadding(); @@ -510,12 +540,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool GLGizmoCut3D::can_perform_cut() const { return true; - - const BoundingBoxf3 box = bounding_box(); - Vec3d plane_center = box.center(); - plane_center.z() = 0; - m_c->object_clipper()->set_range_and_pos(plane_center, - plane_center + m_max_z * Vec3d::UnitZ(), m_cut_z); } void GLGizmoCut3D::perform_cut(const Selection& selection) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index b450b6ec7..80b128cbe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -113,16 +113,14 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void shift_cut_z(double delta); + void update_clipper(); protected: bool on_init() override; - void on_load(cereal::BinaryInputArchive& ar) override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } - void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_load(cereal::BinaryInputArchive& ar) override { ar(/*m_cut_z, */m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_save(cereal::BinaryOutputArchive& ar) const override { ar(/*m_cut_z, */m_keep_upper, m_keep_lower, m_rotate_lower); } std::string on_get_name() const override; void on_set_state() override; - bool on_is_activable() const override; - void on_start_dragging() override; - void on_update(const UpdateData& data) override; CommonGizmosDataID on_get_requirements() const override; void on_set_hover_id() override; void on_enable_grabber(unsigned int id) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 48a54fbae..c4828da8b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -458,9 +458,15 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos) { + std::cout << "origin:\t"<< origin.x() << "\t" << origin.y() << "\t"<< origin.z() << "\n"; + std::cout << "end:\t" << end.x() << "\t" << end.y() << "\t"<< end.z() << "\n"; + Vec3d normal = end-origin; double norm = normal.norm(); pos = std::clamp(pos, 0.0001, norm); + + std::cout << "NORM:\t" << norm << "\tPOS:\t" << pos << "\n\n"; + m_clp.reset(new ClippingPlane(normal, pos)); m_clp_ratio = pos/norm; get_pool()->get_canvas()->set_as_dirty(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index b2d543c5d..c87a2ba45 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -446,7 +446,7 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p else if (m_current == MmuSegmentation) return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else if (m_current == Cut) - return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else return false; } From 9917b8e58b4b5ad77ce18d8e156874600f1a26bf Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 17 Feb 2022 15:16:47 +0100 Subject: [PATCH 09/93] Cut: fixed clipping plane when it is not horizonal --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 11 +++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 208549bc4..f365323ab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -130,9 +130,9 @@ void GLGizmoCut3D::update_clipper() { const Vec3d& angles = m_rotation_gizmo.get_rotation(); Matrix3d m; - m = Eigen::AngleAxisd(angles[X], Vec3d::UnitX()) + m = Eigen::AngleAxisd(angles[Z], Vec3d::UnitZ()) * Eigen::AngleAxisd(angles[Y], Vec3d::UnitY()) - * Eigen::AngleAxisd(angles[Z], Vec3d::UnitZ()); + * Eigen::AngleAxisd(angles[X], Vec3d::UnitX()); Vec3d plane_center = m_move_gizmo.get_center(); BoundingBoxf3 box = m_move_gizmo.bounding_box(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index c4828da8b..7f5567d83 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -458,17 +458,12 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos) { - std::cout << "origin:\t"<< origin.x() << "\t" << origin.y() << "\t"<< origin.z() << "\n"; - std::cout << "end:\t" << end.x() << "\t" << end.y() << "\t"<< end.z() << "\n"; - Vec3d normal = end-origin; double norm = normal.norm(); pos = std::clamp(pos, 0.0001, norm); - - std::cout << "NORM:\t" << norm << "\tPOS:\t" << pos << "\n\n"; - - m_clp.reset(new ClippingPlane(normal, pos)); - m_clp_ratio = pos/norm; + normal.normalize(); + m_clp.reset(new ClippingPlane(normal, normal.dot(origin)+pos)); + m_clp_ratio = pos; get_pool()->get_canvas()->set_as_dirty(); } From 1b9f42d71bc81ab1a12da06f64063de3206286a6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 23 Feb 2022 13:24:06 +0100 Subject: [PATCH 10/93] Cut improvements: * Added new cut() function witch respects to the rotation of the cut plane * Added revert buttons to the GizmoCutDialog * Fixed GLGizmoCenterMove::bounding_box(). Pad and supports don't added to the bb now --- src/imgui/imconfig.h | 2 + src/libslic3r/Model.cpp | 190 +++++++++++++++++++++++++++ src/libslic3r/Model.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 107 ++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 14 +- src/slic3r/GUI/ImGuiWrapper.cpp | 2 + src/slic3r/GUI/Plater.cpp | 24 ++++ src/slic3r/GUI/Plater.hpp | 1 + 8 files changed, 318 insertions(+), 23 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index f2c3ef083..856d29318 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -140,6 +140,8 @@ namespace ImGui const wchar_t CancelButton = 0x14; const wchar_t CancelHoverButton = 0x15; // const wchar_t VarLayerHeightMarker = 0x16; + const wchar_t RevertButton = 0x16; + const wchar_t RevertButton2 = 0x17; const wchar_t RightArrowButton = 0x18; const wchar_t RightArrowHoverButton = 0x19; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 67450fb11..8d44f3e7c 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1336,6 +1336,196 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr return res; } +ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) +{ + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) + return {}; + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; + + // Clone the object to duplicate instances, materials etc. + ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr; + ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr; + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + upper->set_model(nullptr); + upper->sla_support_points.clear(); + upper->sla_drain_holes.clear(); + upper->sla_points_status = sla::PointsStatus::NoPoints; + upper->clear_volumes(); + upper->input_file.clear(); + } + + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + lower->set_model(nullptr); + lower->sla_support_points.clear(); + lower->sla_drain_holes.clear(); + lower->sla_points_status = sla::PointsStatus::NoPoints; + lower->clear_volumes(); + lower->input_file.clear(); + } + + // Because transformations are going to be applied to meshes directly, + // we reset transformation of all instances and volumes, + // except for translation and Z-rotation on instances, which are preserved + // in the transformation matrix and not applied to the mesh transform. + + // const auto instance_matrix = instances[instance]->get_matrix(true); + const auto instance_matrix = Geometry::assemble_transform( + Vec3d::Zero(), // don't apply offset + instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), + instances[instance]->get_scaling_factor(), + instances[instance]->get_mirror() + ); + + const auto cut_matrix = Geometry::assemble_transform( + -cut_center, + Vec3d::Zero(), + Vec3d::Ones(), + Vec3d::Ones() + ); + + const auto invert_cut_matrix = Geometry::assemble_transform( + cut_center, + cut_rotation, + Vec3d::Ones(), + Vec3d::Ones() + ); + + // Displacement (in instance coordinates) to be applied to place the upper parts + Vec3d local_displace = Vec3d::Zero(); + + for (ModelVolume* volume : volumes) { + const auto volume_matrix = volume->get_matrix(); + + volume->supported_facets.reset(); + volume->seam_facets.reset(); + volume->mmu_segmentation_facets.reset(); + + if (!volume->is_model_part()) { + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + + volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower->add_volume(*volume); + } + else if (!volume->mesh().empty()) { + // Transform the mesh by the combined transformation matrix. + // Flip the triangles in case the composite transformation is left handed. + TriangleMesh mesh(volume->mesh()); + mesh.transform(cut_matrix * instance_matrix * volume_matrix, true); + mesh.rotate(-cut_rotation.z(), Z); + mesh.rotate(-cut_rotation.y(), Y); + mesh.rotate(-cut_rotation.x(), X); + + volume->reset_mesh(); + // Reset volume transformation except for offset + const Vec3d offset = volume->get_offset(); + volume->set_transformation(Geometry::Transformation()); + volume->set_offset(offset); + + // Perform cut + TriangleMesh upper_mesh, lower_mesh; + { + indexed_triangle_set upper_its, lower_its; + cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper_mesh = TriangleMesh(upper_its); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower_mesh = TriangleMesh(lower_its); + } + + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { + upper_mesh.transform(invert_cut_matrix); + + ModelVolume* vol = upper->add_volume(upper_mesh); + vol->name = volume->name; + // Don't copy the config's ID. + vol->config.assign_config(volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != volume->config.id()); + vol->set_material(volume->material_id(), *volume->material()); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { + lower_mesh.transform(invert_cut_matrix); + + ModelVolume* vol = lower->add_volume(lower_mesh); + vol->name = volume->name; + // Don't copy the config's ID. + vol->config.assign_config(volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != volume->config.id()); + vol->set_material(volume->material_id(), *volume->material()); + + // Compute the displacement (in instance coordinates) to be applied to place the upper parts + // The upper part displacement is set to half of the lower part bounding box + // this is done in hope at least a part of the upper part will always be visible and draggable + local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); + } + } + } + + ModelObjectPtrs res; + + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { + if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { + upper->center_around_origin(); + upper->translate_instances(-upper->origin_translation); + upper->origin_translation = Vec3d::Zero(); + } + else { + upper->invalidate_bounding_box(); + upper->center_around_origin(); + } + + // Reset instance transformation except offset and Z-rotation + for (size_t i = 0; i < instances.size(); ++i) { + auto& obj_instance = upper->instances[i]; + const Vec3d offset = obj_instance->get_offset(); + const double rot_z = obj_instance->get_rotation().z(); + const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace; + + obj_instance->set_transformation(Geometry::Transformation()); + obj_instance->set_offset(offset + displace); + if (i != instance) + obj_instance->set_rotation(Vec3d(0.0, 0.0, rot_z)); + } + + res.push_back(upper); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { + if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { + lower->center_around_origin(); + lower->translate_instances(-lower->origin_translation); + lower->origin_translation = Vec3d::Zero(); + } + else { + lower->invalidate_bounding_box(); + lower->center_around_origin(); + } + + // Reset instance transformation except offset and Z-rotation + for (size_t i = 0; i < instances.size(); ++i) { + auto& obj_instance = lower->instances[i]; + const Vec3d offset = obj_instance->get_offset(); + const double rot_z = obj_instance->get_rotation().z(); + obj_instance->set_transformation(Geometry::Transformation()); + obj_instance->set_offset(offset); + obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z)); + } + + res.push_back(lower); + } + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + + return res; +} + void ModelObject::split(ModelObjectPtrs* new_objects) { for (ModelVolume* volume : this->volumes) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 7a1cf206e..3d933c470 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -354,6 +354,7 @@ public: size_t facets_count() const; size_t parts_count() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); + ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); void merge(); // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index f365323ab..1e4b9ea2c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -34,6 +34,8 @@ void GLGizmoCenterMove::set_center_pos(const Vec3d& centre_pos) set_center(Vec3d(std::clamp(centre_pos.x(), m_min_pos.x(), m_max_pos.x()), std::clamp(centre_pos.y(), m_min_pos.y(), m_max_pos.y()), std::clamp(centre_pos.z(), m_min_pos.z(), m_max_pos.z()))); + + m_center_offset = get_center() - m_bb_center; } std::string GLGizmoCenterMove::get_tooltip() const @@ -55,14 +57,8 @@ std::string GLGizmoCenterMove::get_tooltip() const void GLGizmoCenterMove::on_set_state() { // Reset internal variables on gizmo activation, if bounding box was changed - if (get_state() == On) { - const BoundingBoxf3 box = bounding_box(); - if (m_max_pos != box.max && m_min_pos != box.min) { - m_max_pos = box.max; - m_min_pos = box.min; - set_center_pos(box.center()); - } - } + if (get_state() == On) + update_bb(); } void GLGizmoCenterMove::on_update(const UpdateData& data) @@ -78,12 +74,26 @@ BoundingBoxf3 GLGizmoCenterMove::bounding_box() const const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int i : idxs) { const GLVolume* volume = selection.get_volume(i); - if (!volume->is_modifier) + // respect just to the solid parts for FFF and ignore pad and supports for SLA + if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) ret.merge(volume->transformed_convex_hull_bounding_box()); } return ret; } +bool GLGizmoCenterMove::update_bb() +{ + const BoundingBoxf3 box = bounding_box(); + if (m_max_pos != box.max && m_min_pos != box.min) { + m_max_pos = box.max; + m_min_pos = box.min; + m_bb_center = box.center(); + set_center_pos(m_bb_center + m_center_offset); + return true; + } + + return false; +} GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) @@ -137,7 +147,7 @@ void GLGizmoCut3D::update_clipper() Vec3d plane_center = m_move_gizmo.get_center(); BoundingBoxf3 box = m_move_gizmo.bounding_box(); - Vec3d min, max = min = plane_center = m_move_gizmo.get_center(); + Vec3d min, max = min = plane_center; min[Z] = box.min.z(); max[Z] = box.max.z(); @@ -155,10 +165,17 @@ void GLGizmoCut3D::update_clipper() m_c->object_clipper()->set_range_and_pos(beg, end, dist); } +void GLGizmoCut3D::update_clipper_on_render() +{ + update_clipper(); + suppress_update_clipper_on_render = true; +} + void GLGizmoCut3D::set_center(const Vec3d& center) { m_move_gizmo.set_center_pos(center); m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + update_clipper(); } void GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) @@ -253,8 +270,11 @@ void GLGizmoCut3D::render_rotation_input(int axis) ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); - rotation[axis] = Geometry::deg2rad(value); - m_rotation_gizmo.set_rotation(rotation); + if (double val = Geometry::deg2rad(value); val != rotation[axis]) { + rotation[axis] = val; + m_rotation_gizmo.set_rotation(rotation); + update_clipper(); + } } void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type) @@ -273,6 +293,30 @@ void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode) m_connector_mode = mode; } +bool GLGizmoCut3D::render_revert_button(const wxString& label) +{ + const ImGuiStyle& style = ImGui::GetStyle(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); + ImGui::SameLine(m_label_width); + + ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f }); + + bool revert = m_imgui->button(label); + + ImGui::PopStyleColor(3); + + if (ImGui::IsItemHovered()) + m_imgui->tooltip(into_u8(_L("Revert")).c_str(), ImGui::GetFontSize() * 20.0f); + + ImGui::PopStyleVar(); + + ImGui::SameLine(); + return revert; +} + void GLGizmoCut3D::render_cut_plane() { const BoundingBoxf3 box = m_move_gizmo.bounding_box(); @@ -365,6 +409,8 @@ void GLGizmoCut3D::on_set_state() m_move_gizmo.set_state(m_state); m_rotation_gizmo.set_center(m_move_gizmo.get_center()); m_rotation_gizmo.set_state(m_state); + + suppress_update_clipper_on_render = m_state != On; } void GLGizmoCut3D::on_set_hover_id() @@ -388,7 +434,7 @@ void GLGizmoCut3D::on_disable_grabber(unsigned int id) bool GLGizmoCut3D::on_is_activable() const { - return m_move_gizmo.is_activable(); + return m_rotation_gizmo.is_activable() && m_move_gizmo.is_activable(); } void GLGizmoCut3D::on_start_dragging() @@ -408,11 +454,16 @@ void GLGizmoCut3D::on_update(const UpdateData& data) { m_move_gizmo.update(data); m_rotation_gizmo.update(data); + update_clipper(); } void GLGizmoCut3D::on_render() { - update_clipper(); + if (m_move_gizmo.update_bb()) { + m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + update_clipper_on_render(); + } + render_cut_plane(); if (m_mode == CutMode::cutPlanar) { int move_group_id = m_move_gizmo.get_group_id(); @@ -421,6 +472,9 @@ void GLGizmoCut3D::on_render() if (m_hover_id == -1 || m_hover_id >= move_group_id) m_move_gizmo.render(); } + + if (!suppress_update_clipper_on_render) + update_clipper_on_render(); } void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) @@ -449,20 +503,23 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_combo(_u8L("Mode"), m_modes, m_mode); + bool revert_rotation{ false }; + bool revert_move{ false }; + if (m_mode <= CutMode::cutByLine) { ImGui::Separator(); if (m_mode == CutMode::cutPlanar) { ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Move center")); - ImGui::SameLine(m_label_width); + revert_move = render_revert_button(ImGui::RevertButton); for (Axis axis : {X, Y, Z}) render_move_center_input(axis); - m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Rotation")); - ImGui::SameLine(m_label_width); + revert_rotation = render_revert_button(ImGui::RevertButton2); for (Axis axis : {X, Y, Z}) render_rotation_input(axis); m_imgui->text(_L("°")); @@ -524,7 +581,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) //////// static bool hide_clipped = true; static bool fill_cut = true; - static float contour_width = 0.; + static float contour_width = 0.2f; m_imgui->checkbox("hide_clipped", hide_clipped); m_imgui->checkbox("fill_cut", fill_cut); m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); @@ -535,6 +592,13 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (cut_clicked && (m_keep_upper || m_keep_lower)) perform_cut(m_parent.get_selection()); + + if (revert_move) + set_center(m_move_gizmo.bounding_box().center()); + if (revert_rotation) { + m_rotation_gizmo.set_rotation(Vec3d::Zero()); + update_clipper(); + } } bool GLGizmoCut3D::can_perform_cut() const @@ -553,8 +617,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); const double object_cut_z = m_move_gizmo.get_center().z() - first_glvolume->get_sla_shift_z(); + Vec3d instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); + + Vec3d cut_center_offset = m_move_gizmo.get_center() - instance_offset; + cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); + if (0.0 < object_cut_z && can_perform_cut()) - wxGetApp().plater()->cut(object_idx, instance_idx, object_cut_z, + wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 80b128cbe..fcf024db1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -19,8 +19,10 @@ public: static const double Margin; private: - Vec3d m_min_pos{ Vec3d::Zero() }; - Vec3d m_max_pos{ Vec3d::Zero() }; + Vec3d m_min_pos { Vec3d::Zero() }; + Vec3d m_max_pos { Vec3d::Zero() }; + Vec3d m_bb_center { Vec3d::Zero() }; + Vec3d m_center_offset { Vec3d::Zero() }; public: GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -33,6 +35,7 @@ protected: public: void set_center_pos(const Vec3d& center_pos); BoundingBoxf3 bounding_box() const; + bool update_bb(); }; @@ -56,6 +59,7 @@ class GLGizmoCut3D : public GLGizmoBase float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; bool m_imperial_units{ false }; + bool suppress_update_clipper_on_render{false}; enum CutMode { cutPlanar @@ -114,11 +118,12 @@ public: void shift_cut_z(double delta); void update_clipper(); + void update_clipper_on_render(); protected: bool on_init() override; - void on_load(cereal::BinaryInputArchive& ar) override { ar(/*m_cut_z, */m_keep_upper, m_keep_lower, m_rotate_lower); } - void on_save(cereal::BinaryOutputArchive& ar) const override { ar(/*m_cut_z, */m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_load(cereal::BinaryInputArchive& ar) override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); } std::string on_get_name() const override; void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; @@ -144,6 +149,7 @@ private: void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(ConnectorMode mode); + bool render_revert_button(const wxString& label); void render_connect_type_radio_button(ConnectorType type); bool can_perform_cut() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e70c1111b..5e154e48a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -70,6 +70,8 @@ static const std::map font_icons = { {ImGui::LegendShells , "legend_shells" }, {ImGui::LegendToolMarker , "legend_toolmarker" }, #endif // ENABLE_LEGEND_TOOLBAR_ICONS + {ImGui::RevertButton , "undo" }, + {ImGui::RevertButton2 , "undo" }, }; static const std::map font_icons_large = { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 32cd327a8..0ff3c294c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5854,6 +5854,30 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCut selection.add_object((unsigned int)(last_id - i), i == 0); } +void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) +{ + 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"); + + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) + return; + + Plater::TakeSnapshot snapshot(this, _L("Cut by Plane")); + wxBusyCursor wait; + + const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes); + + remove(obj_idx); + p->load_model_objects(new_objects); + + Selection& selection = p->get_selection(); + size_t last_id = p->model.objects.size() - 1; + for (size_t i = 0; i < new_objects.size(); ++i) + selection.add_object((unsigned int)(last_id - i), i == 0); +} + void Plater::export_gcode(bool prefer_removable) { if (p->model.objects.empty()) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index baa54480c..a168c32d1 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -254,6 +254,7 @@ public: void toggle_layers_editing(bool enable); void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes); + void cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void export_gcode(bool prefer_removable); void export_stl_obj(bool extended = false, bool selection_only = false); From bf6abf71d081b019d9f2abf102c6808a3cf5036f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 28 Feb 2022 12:06:56 +0100 Subject: [PATCH 11/93] Cut: + Code refactoring: grabbers to move cut plane by Axes are changed to one "plane grabber" + Code cleaning in GizmoMove3D: reverted changes from c45c0045 --- src/imgui/imconfig.h | 1 - src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 365 ++++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 55 ++-- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 22 +- src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 6 - src/slic3r/GUI/ImGuiWrapper.cpp | 1 - 7 files changed, 251 insertions(+), 202 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 856d29318..dcb2d2338 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -141,7 +141,6 @@ namespace ImGui const wchar_t CancelHoverButton = 0x15; // const wchar_t VarLayerHeightMarker = 0x16; const wchar_t RevertButton = 0x16; - const wchar_t RevertButton2 = 0x17; const wchar_t RightArrowButton = 0x18; const wchar_t RightArrowHoverButton = 0x19; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index e0e018a13..4c82c9d5d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -81,7 +81,8 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u void GLGizmoBase::set_hover_id(int id) { - if (m_grabbers.empty() || id < (int)m_grabbers.size()) { +// !??? if (m_grabbers.empty() || id < (int)m_grabbers.size()) + { m_hover_id = id; on_set_hover_id(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 1e4b9ea2c..1d16ee17f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -21,87 +21,17 @@ namespace Slic3r { namespace GUI { -const double GLGizmoCenterMove::Margin = 20.0; - -GLGizmoCenterMove::GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoMove3D(parent, "", -1) -{ -} - -void GLGizmoCenterMove::set_center_pos(const Vec3d& centre_pos) -{ - // Clamp the center position of the cut plane to the object's bounding box - set_center(Vec3d(std::clamp(centre_pos.x(), m_min_pos.x(), m_max_pos.x()), - std::clamp(centre_pos.y(), m_min_pos.y(), m_max_pos.y()), - std::clamp(centre_pos.z(), m_min_pos.z(), m_max_pos.z()))); - - m_center_offset = get_center() - m_bb_center; -} - -std::string GLGizmoCenterMove::get_tooltip() const -{ - double koef = wxGetApp().app_config->get("use_inches") == "1" ? ObjectManipulation::mm_to_in : 1.0; - - const Vec3d& center_pos = get_center(); - - if (m_hover_id == 0 || m_grabbers[0].dragging) - return "X: " + format(center_pos.x() * koef, 2); - else if (m_hover_id == 1 || m_grabbers[1].dragging) - return "Y: " + format(center_pos.y() * koef, 2); - else if (m_hover_id == 2 || m_grabbers[2].dragging) - return "Z: " + format(center_pos.z() * koef, 2); - else - return ""; -} - -void GLGizmoCenterMove::on_set_state() -{ - // Reset internal variables on gizmo activation, if bounding box was changed - if (get_state() == On) - update_bb(); -} - -void GLGizmoCenterMove::on_update(const UpdateData& data) -{ - GLGizmoMove3D::on_update(data); - set_center_pos(get_center()); -} - -BoundingBoxf3 GLGizmoCenterMove::bounding_box() const -{ - BoundingBoxf3 ret; - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int i : idxs) { - const GLVolume* volume = selection.get_volume(i); - // respect just to the solid parts for FFF and ignore pad and supports for SLA - if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) - ret.merge(volume->transformed_convex_hull_bounding_box()); - } - return ret; -} - -bool GLGizmoCenterMove::update_bb() -{ - const BoundingBoxf3 box = bounding_box(); - if (m_max_pos != box.max && m_min_pos != box.min) { - m_max_pos = box.max; - m_min_pos = box.min; - m_bb_center = box.center(); - set_center_pos(m_bb_center + m_center_offset); - return true; - } - - return false; -} - +static const double Margin = 20.0; +static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) , m_rotation_gizmo(GLGizmoRotate3D(parent, "", -1)) - , m_move_gizmo(GLGizmoCenterMove(parent, "", -1)) + , m_rotation_matrix( Eigen::AngleAxisd(0.0, Vec3d::UnitZ()) + * Eigen::AngleAxisd(0.0, Vec3d::UnitY()) + * Eigen::AngleAxisd(0.0, Vec3d::UnitX())) { - m_move_gizmo.set_group_id(3); + set_group_id(3); m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -124,43 +54,53 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, std::string GLGizmoCut3D::get_tooltip() const { std::string tooltip = m_rotation_gizmo.get_tooltip(); - if (tooltip.empty()) - tooltip = m_move_gizmo.get_tooltip(); + if (tooltip.empty()) { + double koef = wxGetApp().app_config->get("use_inches") == "1" ? ObjectManipulation::mm_to_in : 1.0; + if (m_hover_id == get_group_id() || m_grabbers[0].dragging) + return "X: " + format(m_plane_center.x() * koef, 2) + "; " +//"\n" + + "Y: " + format(m_plane_center.y() * koef, 2) + "; " +//"\n" + + "Z: " + format(m_plane_center.z() * koef, 2); + } + return tooltip; } void GLGizmoCut3D::shift_cut_z(double delta) { - Vec3d new_cut_center = m_move_gizmo.get_center(); + Vec3d new_cut_center = m_plane_center; new_cut_center[Z] += delta; - set_center(new_cut_center); + set_center_pos(new_cut_center); +} + +void GLGizmoCut3D::rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center) +{ + if (m_rotations != angles) { + m_rotation_matrix = Eigen::AngleAxisd(angles[Z], Vec3d::UnitZ()) + * Eigen::AngleAxisd(angles[Y], Vec3d::UnitY()) + * Eigen::AngleAxisd(angles[X], Vec3d::UnitX()); + m_rotations = angles; + } + + vec -= center; + vec = m_rotation_matrix * vec; + vec += center; } void GLGizmoCut3D::update_clipper() { const Vec3d& angles = m_rotation_gizmo.get_rotation(); - Matrix3d m; - m = Eigen::AngleAxisd(angles[Z], Vec3d::UnitZ()) - * Eigen::AngleAxisd(angles[Y], Vec3d::UnitY()) - * Eigen::AngleAxisd(angles[X], Vec3d::UnitX()); + BoundingBoxf3 box = bounding_box(); - Vec3d plane_center = m_move_gizmo.get_center(); - BoundingBoxf3 box = m_move_gizmo.bounding_box(); + double radius = box.radius(); - Vec3d min, max = min = plane_center; - min[Z] = box.min.z(); - max[Z] = box.max.z(); + Vec3d beg, end = beg = m_plane_center; + beg[Z] = box.center().z() - radius;//box.min.z(); + end[Z] = box.center().z() + radius;//box.max.z(); - min -= plane_center; - max -= plane_center; + rotate_vec3d_around_center(beg, angles, m_plane_center); + rotate_vec3d_around_center(end, angles, m_plane_center); - Vec3d beg = m * min; - Vec3d end = m * max; - - beg += plane_center; - end += plane_center; - - double dist = (plane_center - beg).norm(); + double dist = (m_plane_center - beg).norm(); m_c->object_clipper()->set_range_and_pos(beg, end, dist); } @@ -173,8 +113,8 @@ void GLGizmoCut3D::update_clipper_on_render() void GLGizmoCut3D::set_center(const Vec3d& center) { - m_move_gizmo.set_center_pos(center); - m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + set_center_pos(center); + m_rotation_gizmo.set_center(m_plane_center); update_clipper(); } @@ -241,7 +181,7 @@ void GLGizmoCut3D::render_move_center_input(int axis) ImGui::SameLine(); ImGui::PushItemWidth(0.3*m_control_width); - Vec3d move = m_move_gizmo.get_center(); + Vec3d move = m_plane_center; double in_val, value = in_val = move[axis]; if (m_imperial_units) value *= ObjectManipulation::mm_to_in; @@ -293,7 +233,7 @@ void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode) m_connector_mode = mode; } -bool GLGizmoCut3D::render_revert_button(const wxString& label) +bool GLGizmoCut3D::render_revert_button(const std::string& label_id) { const ImGuiStyle& style = ImGui::GetStyle(); @@ -304,7 +244,9 @@ bool GLGizmoCut3D::render_revert_button(const wxString& label) ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f }); - bool revert = m_imgui->button(label); + std::string label; + label += ImGui::RevertButton; + bool revert = ImGui::Button((label + "##" + label_id).c_str()); ImGui::PopStyleColor(3); @@ -319,15 +261,18 @@ bool GLGizmoCut3D::render_revert_button(const wxString& label) void GLGizmoCut3D::render_cut_plane() { - const BoundingBoxf3 box = m_move_gizmo.bounding_box(); - Vec3d plane_center = m_move_gizmo.get_center();// == Vec3d::Zero() ? box.center() : m_move_gizmo.get_center(); - m_c->object_clipper()->render_cut(); - const float min_x = box.min.x() - GLGizmoCenterMove::Margin - plane_center.x(); - const float max_x = box.max.x() + GLGizmoCenterMove::Margin - plane_center.x(); - const float min_y = box.min.y() - GLGizmoCenterMove::Margin - plane_center.y(); - const float max_y = box.max.y() + GLGizmoCenterMove::Margin - plane_center.y(); + if (m_hide_cut_plane) + return; + + const BoundingBoxf3 box = bounding_box(); + + const float min_x = box.min.x() - Margin - m_plane_center.x(); + const float max_x = box.max.x() + Margin - m_plane_center.x(); + const float min_y = box.min.y() - Margin - m_plane_center.y(); + const float max_y = box.max.y() + Margin - m_plane_center.y(); + glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glEnable(GL_BLEND)); @@ -339,10 +284,10 @@ void GLGizmoCut3D::render_cut_plane() return; shader->start_using(); - Vec3d angles = m_rotation_gizmo.get_rotation(); + const Vec3d& angles = m_rotation_gizmo.get_rotation(); glsafe(::glPushMatrix()); - glsafe(::glTranslated(plane_center.x(), plane_center.y(), plane_center.z())); + glsafe(::glTranslated(m_plane_center.x(), m_plane_center.y(), m_plane_center.z())); glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); @@ -388,14 +333,70 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } +void GLGizmoCut3D::render_cut_center_graber() +{ + const Vec3d& angles = m_rotation_gizmo.get_rotation(); + const BoundingBoxf3 box = bounding_box(); + + Vec3d grabber_center = m_plane_center; + grabber_center[Z] += 10; // Margin + + rotate_vec3d_around_center(grabber_center, angles, m_plane_center); + + m_grabbers[0].center = grabber_center; + m_grabbers[0].angles = angles; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glLineWidth(m_hover_id == get_group_id() ? 2.0f : 1.5f)); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); + // if (!m_grabber_connection.is_initialized() || z_changed) + { + m_grabber_connection.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3, GLModel::Geometry::EIndexType::USHORT }; + init_data.color = ColorRGBA::YELLOW(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex((Vec3f)m_plane_center.cast()); + init_data.add_vertex((Vec3f)m_grabbers[0].center.cast()); + + // indices + init_data.add_ushort_line(0, 1); + + m_grabber_connection.init_from(std::move(init_data)); + } + m_grabber_connection.render(); + + shader->stop_using(); + } + + shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + + m_grabbers[0].color = GRABBER_COLOR; + m_grabbers[0].render(m_hover_id == get_group_id(), float((box.size().x() + box.size().y() + box.size().z()) / 3.0)); + + shader->stop_using(); + } +} + bool GLGizmoCut3D::on_init() { + m_grabbers.emplace_back(); + m_shortcut_key = WXK_CONTROL_C; + if(!m_rotation_gizmo.init()) return false; - if(!m_move_gizmo.init()) - return false; - m_shortcut_key = WXK_CONTROL_C; return true; } @@ -406,8 +407,9 @@ std::string GLGizmoCut3D::on_get_name() const void GLGizmoCut3D::on_set_state() { - m_move_gizmo.set_state(m_state); - m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + if (get_state() == On) + update_bb(); + m_rotation_gizmo.set_center(m_plane_center); m_rotation_gizmo.set_state(m_state); suppress_update_clipper_on_render = m_state != On; @@ -415,68 +417,138 @@ void GLGizmoCut3D::on_set_state() void GLGizmoCut3D::on_set_hover_id() { - int move_group_id = m_move_gizmo.get_group_id(); - m_rotation_gizmo. set_hover_id((m_hover_id < move_group_id) ? m_hover_id : -1); - m_move_gizmo. set_hover_id((m_hover_id >= move_group_id) ? m_hover_id - move_group_id : -1); + m_rotation_gizmo.set_hover_id(m_hover_id < get_group_id() ? m_hover_id: -1); } void GLGizmoCut3D::on_enable_grabber(unsigned int id) { m_rotation_gizmo.enable_grabber(id); - m_move_gizmo.enable_grabber(id- m_move_gizmo.get_group_id()); + if (id == get_group_id()) + m_grabbers[0].enabled = true; } void GLGizmoCut3D::on_disable_grabber(unsigned int id) { m_rotation_gizmo.disable_grabber(id); - m_move_gizmo.disable_grabber(id- m_move_gizmo.get_group_id()); + if (id == get_group_id()) + m_grabbers[0].enabled = false; } bool GLGizmoCut3D::on_is_activable() const { - return m_rotation_gizmo.is_activable() && m_move_gizmo.is_activable(); + // This is assumed in GLCanvas3D::do_rotate, do not change this + // without updating that function too. + return m_parent.get_selection().is_single_full_instance(); } void GLGizmoCut3D::on_start_dragging() { m_rotation_gizmo.start_dragging(); - m_move_gizmo.start_dragging(); } void GLGizmoCut3D::on_stop_dragging() { m_rotation_gizmo.stop_dragging(); - m_rotation_gizmo.set_center(m_move_gizmo.get_center()); - m_move_gizmo.stop_dragging(); } void GLGizmoCut3D::on_update(const UpdateData& data) { - m_move_gizmo.update(data); - m_rotation_gizmo.update(data); - update_clipper(); + if (m_hover_id == get_group_id()) { + const Vec3d& starting_box_center = m_plane_center; + const Vec3d& starting_drag_position = m_grabbers[0].center; + + double projection = 0.0; + + Vec3d starting_vec = starting_drag_position - starting_box_center; + if (starting_vec.norm() != 0.0) { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - starting_drag_position; + + starting_vec.normalize(); + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec); + } + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + set_center(starting_box_center + starting_vec * projection); + } + else { + m_rotation_gizmo.update(data); + update_clipper(); + } +} + +void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) +{ + m_plane_center = center_pos; + + // !!! ysFIXME add smart clamp calculation + // Clamp the center position of the cut plane to the object's bounding box + //m_plane_center = Vec3d(std::clamp(center_pos.x(), m_min_pos.x(), m_max_pos.x()), + // std::clamp(center_pos.y(), m_min_pos.y(), m_max_pos.y()), + // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z()))); + + m_center_offset = m_plane_center - m_bb_center; +} + +BoundingBoxf3 GLGizmoCut3D::bounding_box() const +{ + BoundingBoxf3 ret; + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + for (unsigned int i : idxs) { + const GLVolume* volume = selection.get_volume(i); + // respect just to the solid parts for FFF and ignore pad and supports for SLA + if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) + ret.merge(volume->transformed_convex_hull_bounding_box()); + } + return ret; +} + +bool GLGizmoCut3D::update_bb() +{ + const BoundingBoxf3 box = bounding_box(); + if (m_max_pos != box.max && m_min_pos != box.min) { + m_max_pos = box.max; + m_min_pos = box.min; + m_bb_center = box.center(); + set_center_pos(m_bb_center + m_center_offset); + return true; + } + return false; } void GLGizmoCut3D::on_render() { - if (m_move_gizmo.update_bb()) { - m_rotation_gizmo.set_center(m_move_gizmo.get_center()); + if (update_bb()) { + m_rotation_gizmo.set_center(m_plane_center); update_clipper_on_render(); } render_cut_plane(); + render_cut_center_graber(); if (m_mode == CutMode::cutPlanar) { - int move_group_id = m_move_gizmo.get_group_id(); - if (m_hover_id < move_group_id) + if (m_hover_id < get_group_id()) m_rotation_gizmo.render(); - if (m_hover_id == -1 || m_hover_id >= move_group_id) - m_move_gizmo.render(); } if (!suppress_update_clipper_on_render) update_clipper_on_render(); } +void GLGizmoCut3D::on_render_for_picking() +{ + m_rotation_gizmo.render_for_picking(); + render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); +} + void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) { static float last_y = 0.0f; @@ -512,14 +584,14 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_mode == CutMode::cutPlanar) { ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Move center")); - revert_move = render_revert_button(ImGui::RevertButton); + revert_move = render_revert_button("move"); for (Axis axis : {X, Y, Z}) render_move_center_input(axis); m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Rotation")); - revert_rotation = render_revert_button(ImGui::RevertButton2); + revert_rotation = render_revert_button("rotation"); for (Axis axis : {X, Y, Z}) render_rotation_input(axis); m_imgui->text(_L("°")); @@ -578,6 +650,10 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) const bool cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); + ImGui::Separator(); + + m_imgui->checkbox("hide_cut_plane", m_hide_cut_plane); + //////// static bool hide_clipped = true; static bool fill_cut = true; @@ -594,7 +670,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) perform_cut(m_parent.get_selection()); if (revert_move) - set_center(m_move_gizmo.bounding_box().center()); + set_center(bounding_box().center()); if (revert_rotation) { m_rotation_gizmo.set_rotation(Vec3d::Zero()); update_clipper(); @@ -602,7 +678,12 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } bool GLGizmoCut3D::can_perform_cut() const -{ +{ + BoundingBoxf3 box = bounding_box(); + double dist = (m_plane_center - box.center()).norm(); + if (dist > box.radius()) + return false; + return true; } @@ -615,11 +696,11 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // m_cut_z is the distance from the bed. Subtract possible SLA elevation. const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); - const double object_cut_z = m_move_gizmo.get_center().z() - first_glvolume->get_sla_shift_z(); + const double object_cut_z = m_plane_center.z() - first_glvolume->get_sla_shift_z(); Vec3d instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); - Vec3d cut_center_offset = m_move_gizmo.get_center() - instance_offset; + Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); if (0.0 < object_cut_z && can_perform_cut()) @@ -632,8 +713,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } } - - bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (is_dragging()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index fcf024db1..2c6eddc26 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -13,39 +13,22 @@ namespace GUI { enum class SLAGizmoEventType : unsigned char; -class GLGizmoCenterMove : public GLGizmoMove3D -{ -public: - static const double Margin; -private: - - Vec3d m_min_pos { Vec3d::Zero() }; - Vec3d m_max_pos { Vec3d::Zero() }; - Vec3d m_bb_center { Vec3d::Zero() }; - Vec3d m_center_offset { Vec3d::Zero() }; - -public: - GLGizmoCenterMove(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - std::string get_tooltip() const override; - -protected: - virtual void on_set_state() override; - virtual void on_update(const UpdateData& data) override; - -public: - void set_center_pos(const Vec3d& center_pos); - BoundingBoxf3 bounding_box() const; - bool update_bb(); -}; - - class GLGizmoCut3D : public GLGizmoBase { GLGizmoRotate3D m_rotation_gizmo; - GLGizmoCenterMove m_move_gizmo; + double m_snap_step{ 1.0 }; + + Vec3d m_plane_center{ Vec3d::Zero() }; + // data to check position of the cut palne center on gizmo activation + Vec3d m_min_pos{ Vec3d::Zero() }; + Vec3d m_max_pos{ Vec3d::Zero() }; + Vec3d m_bb_center{ Vec3d::Zero() }; + Vec3d m_center_offset{ Vec3d::Zero() }; + #if ENABLE_GLBEGIN_GLEND_REMOVAL GLModel m_plane; + GLModel m_grabber_connection; float m_old_z{ 0.0f }; #endif // ENABLE_GLBEGIN_GLEND_REMOVAL @@ -53,6 +36,8 @@ class GLGizmoCut3D : public GLGizmoBase bool m_keep_lower{ true }; bool m_rotate_lower{ false }; + bool m_hide_cut_plane{ false }; + double m_connector_depth_ratio{ 1.5 }; double m_connector_size{ 5.0 }; @@ -61,6 +46,9 @@ class GLGizmoCut3D : public GLGizmoBase bool m_imperial_units{ false }; bool suppress_update_clipper_on_render{false}; + Matrix3d m_rotation_matrix; + Vec3d m_rotations{ Vec3d::Zero() }; + enum CutMode { cutPlanar , cutByLine @@ -117,9 +105,12 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void shift_cut_z(double delta); + void rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center); void update_clipper(); void update_clipper_on_render(); + BoundingBoxf3 bounding_box() const; + protected: bool on_init() override; void on_load(cereal::BinaryInputArchive& ar) override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); } @@ -135,10 +126,7 @@ protected: void on_stop_dragging() override; void on_update(const UpdateData& data) override; void on_render() override; - void on_render_for_picking() override { - m_rotation_gizmo.render_for_picking(); - m_move_gizmo.render_for_picking(); - } + void on_render_for_picking() override; void on_render_input_window(float x, float y, float bottom_limit) override; @@ -149,12 +137,15 @@ private: void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(ConnectorMode mode); - bool render_revert_button(const wxString& label); + bool render_revert_button(const std::string& label); void render_connect_type_radio_button(ConnectorType type); bool can_perform_cut() const; void render_cut_plane(); + void render_cut_center_graber(); void perform_cut(const Selection& selection); + void set_center_pos(const Vec3d& center_pos); + bool update_bb(); }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index e12974b75..095234e02 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -78,9 +78,6 @@ void GLGizmoMove3D::on_update(const UpdateData& data) m_displacement.y() = calc_projection(data); else if (m_hover_id == 2) m_displacement.z() = calc_projection(data); - - if (m_has_forced_center) - m_center += m_displacement; } void GLGizmoMove3D::on_render() @@ -94,15 +91,8 @@ void GLGizmoMove3D::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const BoundingBoxf3& box = selection.get_bounding_box(); - const Vec3d& center = m_has_forced_center ? m_center : box.center(); + const Vec3d& center = box.center(); - if (m_has_forced_center) - for (auto axis : { X, Y, Z }) { - m_grabbers[axis].center = center; - m_grabbers[axis].center[axis] += 0.5*fabs(box.max[axis] - box.min[axis]); - m_grabbers[axis].color = AXES_COLOR[axis]; - } - else { // x axis m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; m_grabbers[0].color = AXES_COLOR[0]; @@ -114,7 +104,6 @@ void GLGizmoMove3D::on_render() // z axis m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; m_grabbers[2].color = AXES_COLOR[2]; - } glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); @@ -227,10 +216,7 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - const Vec3d& starting_drag_position = m_has_forced_center ? m_grabbers[m_hover_id].center : m_starting_drag_position; - const Vec3d& starting_box_center = m_has_forced_center ? m_center : m_starting_box_center; - - Vec3d starting_vec = starting_drag_position - starting_box_center; + Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; double len_starting_vec = starting_vec.norm(); if (len_starting_vec != 0.0) { Vec3d mouse_dir = data.mouse_ray.unit_vector(); @@ -238,9 +224,9 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // in our case plane normal and ray direction are the same (orthogonal view) // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; + Vec3d inters_vec = inters - m_starting_drag_position; // finds projection of the vector along the staring direction projection = inters_vec.dot(starting_vec.normalized()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 9e404a95c..2a75df866 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -17,9 +17,6 @@ class GLGizmoMove3D : public GLGizmoBase Vec3d m_starting_box_center{ Vec3d::Zero() }; Vec3d m_starting_box_bottom_center{ Vec3d::Zero() }; - Vec3d m_center{ Vec3d::Zero() }; - bool m_has_forced_center{ false }; - GLModel m_cone; #if ENABLE_GLBEGIN_GLEND_REMOVAL struct GrabberConnection @@ -41,9 +38,6 @@ public: std::string get_tooltip() const override; - void set_center(Vec3d center) { m_center = center; m_has_forced_center = true; } - const Vec3d& get_center() const { return m_center; } - protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 5e154e48a..9a2e5d266 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -71,7 +71,6 @@ static const std::map font_icons = { {ImGui::LegendToolMarker , "legend_toolmarker" }, #endif // ENABLE_LEGEND_TOOLBAR_ICONS {ImGui::RevertButton , "undo" }, - {ImGui::RevertButton2 , "undo" }, }; static const std::map font_icons_large = { From 5d83781780154fcd312502fd6bd2bed40e229d0c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 28 Feb 2022 16:56:10 +0100 Subject: [PATCH 12/93] Fixes after merge with master --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 18 +++-- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 101 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 3 +- 5 files changed, 51 insertions(+), 79 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 82702f212..d503c024e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -84,8 +84,8 @@ void GLGizmoBase::set_hover_id(int id) assert(!m_dragging); // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate - if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) - return; +// if (!m_grabbers.empty() && id >= (int) m_grabbers.size()) +// return; m_hover_id = id; on_set_hover_id(); @@ -160,14 +160,20 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { if (mouse_event.LeftDown()) { Selection &selection = m_parent.get_selection(); - if (!selection.is_empty() && m_hover_id != -1 && - (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { + if (!selection.is_empty() && m_hover_id != -1 /*&& + (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))*/) { selection.setup_cache(); m_dragging = true; for (auto &grabber : m_grabbers) grabber.dragging = false; - if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) - m_grabbers[m_hover_id].dragging = true; + //if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) + // m_grabbers[m_hover_id].dragging = true; + if (!m_grabbers.empty()) { + if (m_hover_id < int(m_grabbers.size())) + m_grabbers[m_hover_id].dragging = true; + else if (m_group_id >= 0 && m_hover_id >= m_group_id) + m_grabbers[m_hover_id - m_group_id].dragging = true; + } // prevent change of hover_id during dragging m_parent.set_mouse_as_dragging(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 08c54e2d2..3dd37ad39 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -31,7 +31,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, * Eigen::AngleAxisd(0.0, Vec3d::UnitY()) * Eigen::AngleAxisd(0.0, Vec3d::UnitX())) { - set_group_id(3); + m_rotation_gizmo.use_only_grabbers(); + m_group_id = 3; m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -56,7 +57,7 @@ std::string GLGizmoCut3D::get_tooltip() const std::string tooltip = m_rotation_gizmo.get_tooltip(); if (tooltip.empty()) { double koef = wxGetApp().app_config->get("use_inches") == "1" ? ObjectManipulation::mm_to_in : 1.0; - if (m_hover_id == get_group_id() || m_grabbers[0].dragging) + if (m_hover_id == m_group_id || m_grabbers[0].dragging) return "X: " + format(m_plane_center.x() * koef, 2) + "; " +//"\n" + "Y: " + format(m_plane_center.y() * koef, 2) + "; " +//"\n" + "Z: " + format(m_plane_center.z() * koef, 2); @@ -65,8 +66,12 @@ std::string GLGizmoCut3D::get_tooltip() const return tooltip; } -bool GLGizmoCut::on_mouse(const wxMouseEvent &mouse_event) +bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { + if (m_rotation_gizmo.on_mouse(mouse_event)) { + update_clipper(); + return true; + } return use_grabbers(mouse_event); } @@ -288,13 +293,6 @@ void GLGizmoCut3D::render_cut_plane() if (shader == nullptr) return; shader->start_using(); - Vec3d diff = plane_center - m_old_center; - // Z changed when move with cut plane - // X and Y changed when move with cutted object - bool is_changed = std::abs(diff.x()) > EPSILON || - std::abs(diff.y()) > EPSILON || - std::abs(diff.z()) > EPSILON; - m_old_center = plane_center; const Vec3d& angles = m_rotation_gizmo.get_rotation(); @@ -360,7 +358,7 @@ void GLGizmoCut3D::render_cut_center_graber() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glLineWidth(m_hover_id == get_group_id() ? 2.0f : 1.5f)); + glsafe(::glLineWidth(m_hover_id == m_group_id ? 2.0f : 1.5f)); GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader != nullptr) { @@ -395,7 +393,7 @@ void GLGizmoCut3D::render_cut_center_graber() shader->set_uniform("emission_factor", 0.1f); m_grabbers[0].color = GRABBER_COLOR; - m_grabbers[0].render(m_hover_id == get_group_id(), float((box.size().x() + box.size().y() + box.size().z()) / 3.0)); + m_grabbers[0].render(m_hover_id == m_group_id, float((box.size().x() + box.size().y() + box.size().z()) / 3.0)); shader->stop_using(); } @@ -429,21 +427,7 @@ void GLGizmoCut3D::on_set_state() void GLGizmoCut3D::on_set_hover_id() { - m_rotation_gizmo.set_hover_id(m_hover_id < get_group_id() ? m_hover_id: -1); -} - -void GLGizmoCut3D::on_enable_grabber(unsigned int id) -{ - m_rotation_gizmo.enable_grabber(id); - if (id == get_group_id()) - m_grabbers[0].enabled = true; -} - -void GLGizmoCut3D::on_disable_grabber(unsigned int id) -{ - m_rotation_gizmo.disable_grabber(id); - if (id == get_group_id()) - m_grabbers[0].enabled = false; + m_rotation_gizmo.set_hover_id(m_hover_id < m_group_id ? m_hover_id: -1); } bool GLGizmoCut3D::on_is_activable() const @@ -453,48 +437,33 @@ bool GLGizmoCut3D::on_is_activable() const return m_parent.get_selection().is_single_full_instance(); } -void GLGizmoCut3D::on_start_dragging() +void GLGizmoCut3D::on_dragging(const UpdateData& data) { - m_rotation_gizmo.start_dragging(); -} + assert(m_hover_id == m_group_id); -void GLGizmoCut3D::on_stop_dragging() -{ - m_rotation_gizmo.stop_dragging(); -} + const Vec3d & starting_box_center = m_plane_center; + const Vec3d & starting_drag_position = m_grabbers[0].center; + double projection = 0.0; -void GLGizmoCut3D::on_update(const UpdateData& data) -{ - if (m_hover_id == get_group_id()) { - const Vec3d& starting_box_center = m_plane_center; - const Vec3d& starting_drag_position = m_grabbers[0].center; + Vec3d starting_vec = starting_drag_position - starting_box_center; + if (starting_vec.norm() != 0.0) { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - starting_drag_position; - double projection = 0.0; - - Vec3d starting_vec = starting_drag_position - starting_box_center; - if (starting_vec.norm() != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; - - starting_vec.normalize(); - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec); - } - if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); - - set_center(starting_box_center + starting_vec * projection); - } - else { - m_rotation_gizmo.update(data); - update_clipper(); + starting_vec.normalize(); + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec); } + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + set_center(starting_box_center + starting_vec * projection); } void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) @@ -547,7 +516,7 @@ void GLGizmoCut3D::on_render() render_cut_plane(); render_cut_center_graber(); if (m_mode == CutMode::cutPlanar) { - if (m_hover_id < get_group_id()) + if (m_hover_id < m_group_id) m_rotation_gizmo.render(); } @@ -606,7 +575,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) revert_rotation = render_revert_button("rotation"); for (Axis axis : {X, Y, Z}) render_rotation_input(axis); - m_imgui->text(_L("°")); + m_imgui->text(_L("°")); } else { ImGui::AlignTextToFramePadding(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 4da400305..995d1f82d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -127,12 +127,8 @@ protected: void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; void on_set_hover_id() override; - void on_enable_grabber(unsigned int id) override; - void on_disable_grabber(unsigned int id) override; bool on_is_activable() const override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_update(const UpdateData& data) override; + void on_dragging(const UpdateData& data) override; void on_render() override; void on_render_for_picking() override; void on_render_input_window(float x, float y, float bottom_limit) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 31f1a24c0..1402c2e45 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -679,7 +679,7 @@ GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_fil bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) { - if (mouse_event.Dragging() && m_dragging) { + if (mouse_event.Dragging() && m_dragging && !m_use_only_grabbers) { // Apply new temporary rotations TransformationType transformation_type( TransformationType::World_Relative_Joint); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 171f61ab2..125fa0730 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -63,7 +63,6 @@ public: double get_angle() const { return m_angle; } void set_angle(double angle); - void set_center_z(double center_z); void set_center(const Vec3d& center); std::string get_tooltip() const override; @@ -118,6 +117,7 @@ private: class GLGizmoRotate3D : public GLGizmoBase { std::array m_gizmos; + bool m_use_only_grabbers{ false }; public: GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -125,6 +125,7 @@ public: Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } void set_center(const Vec3d& center) { m_gizmos[X].set_center(center); m_gizmos[Y].set_center(center); m_gizmos[Z].set_center(center); } + void use_only_grabbers() { m_use_only_grabbers = true; } std::string get_tooltip() const override { std::string tooltip = m_gizmos[X].get_tooltip(); From 0fba32fa5327e6ef7a3708e87e0ebb82cebbf56a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 8 Mar 2022 14:10:25 +0100 Subject: [PATCH 13/93] Cut: Add connectors. WIP --- src/libslic3r/Model.hpp | 43 +++ src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 368 ++++++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 21 +- 4 files changed, 375 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3d933c470..2e96910f4 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -219,6 +219,46 @@ private: friend class ModelObject; }; +struct CutConnector +{ + Vec3f pos; + Vec3f normal; + float radius; + float height; + bool failed = false; + + CutConnector() + : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) + {} + + CutConnector(Vec3f p, Vec3f n, float r, float h, bool fl = false) + : pos(p), normal(n), radius(r), height(h), failed(fl) + {} + + CutConnector(const CutConnector& rhs) : + CutConnector(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {} + + bool operator==(const CutConnector& sp) const; + + bool operator!=(const CutConnector& sp) const { return !(sp == (*this)); } +/* + bool is_inside(const Vec3f& pt) const; + + bool get_intersections(const Vec3f& s, const Vec3f& dir, + std::array, 2>& out) const; + + indexed_triangle_set to_mesh() const; +*/ + template inline void serialize(Archive& ar) + { + ar(pos, normal, radius, height, failed); + } + + static constexpr size_t steps = 32; +}; + +using CutConnectors = std::vector; + // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -269,6 +309,9 @@ public: // Holes to be drilled into the object so resin can flow out sla::DrainHoles sla_drain_holes; + // Connectors to be added into the object after cut + CutConnectors cut_connectors; + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index d503c024e..b188d60af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -171,7 +171,7 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { if (!m_grabbers.empty()) { if (m_hover_id < int(m_grabbers.size())) m_grabbers[m_hover_id].dragging = true; - else if (m_group_id >= 0 && m_hover_id >= m_group_id) + else if (m_group_id >= 0 && m_hover_id < int(m_grabbers.size() + m_group_id)) m_grabbers[m_hover_id - m_group_id].dragging = true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 3dd37ad39..90084b066 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -33,6 +33,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, { m_rotation_gizmo.use_only_grabbers(); m_group_id = 3; + m_connectors_group_id = 4; m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -50,6 +51,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, }; m_axis_names = { "X", "Y", "Z" }; + + update_connector_shape(); } std::string GLGizmoCut3D::get_tooltip() const @@ -68,11 +71,71 @@ std::string GLGizmoCut3D::get_tooltip() const bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { + if (mouse_event.Moving()) + return false; + if (m_rotation_gizmo.on_mouse(mouse_event)) { update_clipper(); return true; } - return use_grabbers(mouse_event); + + if (use_grabbers(mouse_event)) + return true; + + Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); + Vec2d mouse_pos = mouse_coord.cast(); + + static bool pending_right_up = false; + if (mouse_event.LeftDown()) { + bool grabber_contains_mouse = (get_hover_id() != -1); + bool control_down = mouse_event.CmdDown(); + if ((!control_down || grabber_contains_mouse) && + gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) + return true; + } + else if (mouse_event.Dragging()) { + bool control_down = mouse_event.CmdDown(); + if (m_parent.get_move_volume_id() != -1) { + // don't allow dragging objects with the Sla gizmo on + return true; + } + else if (!control_down && + gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) { + // the gizmo got the event and took some action, no need to do + // anything more here + m_parent.set_as_dirty(); + return true; + } + else if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) { + // CTRL has been pressed while already dragging -> stop current action + if (mouse_event.LeftIsDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true); + else if (mouse_event.RightIsDown()) + pending_right_up = false; + } + } + else if (mouse_event.LeftUp() && !m_parent.is_mouse_dragging()) { + // in case SLA/FDM gizmo is selected, we just pass the LeftUp event + // and stop processing - neither object moving or selecting is + // suppressed in that case + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + return true; + } + else if (mouse_event.RightDown()) { + if (m_parent.get_selection().get_object_idx() != -1 && + gizmo_event(SLAGizmoEventType::RightDown, mouse_pos, false, false, false)) { + // we need to set the following right up as processed to avoid showing + // the context menu if the user release the mouse over the object + pending_right_up = true; + // event was taken care of by the SlaSupports gizmo + return true; + } + } + else if (pending_right_up && mouse_event.RightUp()) { + pending_right_up = false; + return true; + } + return false; } void GLGizmoCut3D::shift_cut_z(double delta) @@ -128,7 +191,7 @@ void GLGizmoCut3D::set_center(const Vec3d& center) update_clipper(); } -void GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) +bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx) { ImGui::AlignTextToFramePadding(); m_imgui->text(label); @@ -163,10 +226,13 @@ void GLGizmoCut3D::render_combo(const std::string& label, const std::vectortext(label); @@ -176,12 +242,14 @@ void GLGizmoCut3D::render_double_input(const std::string& label, double& value_i double value = value_in; if (m_imperial_units) value *= ObjectManipulation::mm_to_in; + double old_val = value; ImGui::InputDouble(("##" + label).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); + return old_val != value; } void GLGizmoCut3D::render_move_center_input(int axis) @@ -271,11 +339,6 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id) void GLGizmoCut3D::render_cut_plane() { - m_c->object_clipper()->render_cut(); - - if (m_hide_cut_plane) - return; - const BoundingBoxf3 box = bounding_box(); const float min_x = box.min.x() - Margin - m_plane_center.x(); @@ -349,7 +412,7 @@ void GLGizmoCut3D::render_cut_center_graber() const BoundingBoxf3 box = bounding_box(); Vec3d grabber_center = m_plane_center; - grabber_center[Z] += 10; // Margin + grabber_center[Z] += float(box.radius()/2.0); // Margin rotate_vec3d_around_center(grabber_center, angles, m_plane_center); @@ -439,31 +502,50 @@ bool GLGizmoCut3D::on_is_activable() const void GLGizmoCut3D::on_dragging(const UpdateData& data) { - assert(m_hover_id == m_group_id); + if (m_hover_id < m_group_id) + return; - const Vec3d & starting_box_center = m_plane_center; - const Vec3d & starting_drag_position = m_grabbers[0].center; - double projection = 0.0; + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - Vec3d starting_vec = starting_drag_position - starting_box_center; - if (starting_vec.norm() != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; + if (m_hover_id == m_group_id) { + const Vec3d& starting_box_center = m_plane_center; + const Vec3d& starting_drag_position = m_grabbers[0].center; + double projection = 0.0; - starting_vec.normalize(); - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec); + Vec3d starting_vec = starting_drag_position - starting_box_center; + if (starting_vec.norm() != 0.0) { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - starting_drag_position; + + starting_vec.normalize(); + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec); + } + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + // move cut plane center + set_center(starting_box_center + starting_vec * projection); + + // move connectors + Vec3f shift = Vec3f(starting_vec.cast() * projection); + for (auto& connector : connectors) + connector.pos += shift; + } + else if (m_hover_id > m_group_id) + { + std::pair pos_and_normal; + if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) + return; + connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; + connectors[m_hover_id - m_connectors_group_id].normal = -pos_and_normal.second; } - if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); - - set_center(starting_box_center + starting_vec * projection); } void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) @@ -474,7 +556,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) // Clamp the center position of the cut plane to the object's bounding box //m_plane_center = Vec3d(std::clamp(center_pos.x(), m_min_pos.x(), m_max_pos.x()), // std::clamp(center_pos.y(), m_min_pos.y(), m_max_pos.y()), - // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z()))); + // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z())); m_center_offset = m_plane_center - m_bb_center; } @@ -513,11 +595,17 @@ void GLGizmoCut3D::on_render() update_clipper_on_render(); } - render_cut_plane(); - render_cut_center_graber(); - if (m_mode == CutMode::cutPlanar) { - if (m_hover_id < m_group_id) - m_rotation_gizmo.render(); + render_connectors(false); + + m_c->object_clipper()->render_cut(); + + if (!m_hide_cut_plane) { + render_cut_plane(); + render_cut_center_graber(); + if (m_mode == CutMode::cutPlanar) { + if (m_hover_id < m_group_id) + m_rotation_gizmo.render(); + } } if (!suppress_update_clipper_on_render) @@ -528,6 +616,8 @@ void GLGizmoCut3D::on_render_for_picking() { m_rotation_gizmo.render_for_picking(); render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); + + render_connectors(true); } void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) @@ -617,11 +707,23 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_connect_type_radio_button(ConnectorType::Plug); render_connect_type_radio_button(ConnectorType::Dowel); - render_combo(_u8L("Style"), m_connector_styles, m_connector_style); - render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape); + if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) + update_connector_shape(); + if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) + update_connector_shape(); - render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio); - render_double_input(_u8L("Size"), m_connector_size); + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) + for (auto& connector : connectors) + connector.height = float(m_connector_depth_ratio); + if (render_double_input(_u8L("Size"), m_connector_size)) + for (auto& connector : connectors) + connector.radius = float(m_connector_size * 0.5); + + m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut()); + if (m_imgui->button(_L("Reset connectors"))) + reset_connectors(); + m_imgui->disabled_end(); m_imgui->disabled_end(); @@ -633,7 +735,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); - m_imgui->checkbox("hide_cut_plane", m_hide_cut_plane); + m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); //////// static bool hide_clipped = true; @@ -658,6 +760,87 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } } +void GLGizmoCut3D::render_connectors(bool picking) +{ + const Selection& selection = m_parent.get_selection(); + +#if ENABLE_GLBEGIN_GLEND_REMOVAL + GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + ScopeGuard guard([shader]() { shader->stop_using(); }); +#else + GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); + if (shader) + shader->start_using(); + ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + //const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + //const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); +// glsafe(::glMultMatrixd(instance_matrix.data())); + + ColorRGBA render_color; + const CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + size_t cache_size = connectors.size(); + + for (size_t i = 0; i < cache_size; ++i) { + const CutConnector& connector = connectors[i]; + const bool& point_selected = m_selected[i]; + + // First decide about the color of the point. + if (picking) + render_color = picking_decode(BASE_ID - i - m_connectors_group_id); + else { + if (size_t(m_hover_id- m_connectors_group_id) == i) + render_color = ColorRGBA::CYAN(); + else // neither hover nor picking + render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); + } + +#if ENABLE_GLBEGIN_GLEND_REMOVAL + m_connector_shape.set_color(render_color); +#else + const_cast(&m_connector_shape)->set_color(-1, render_color); +#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); +// glsafe(::glTranslatef(connector.pos.x() - m_plane_center.x(), connector.pos.y() - m_plane_center.y(), connector.pos.z() - m_plane_center.z())); + glsafe(::glTranslatef(connector.pos.x(), connector.pos.y(), connector.pos.z())); +// glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + + if (vol->is_left_handed()) + glFrontFace(GL_CW); + + const Vec3d& angles = m_rotation_gizmo.get_rotation(); + glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); + + // Matrices set, we can render the point mark now. + /* Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-connector.normal).cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z())); +*/ glsafe(::glTranslated(0., 0., -0.5*connector.height)); + glsafe(::glScaled(connector.radius, connector.radius, connector.height)); + m_connector_shape.render(); + + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + glsafe(::glPopMatrix()); + } + + glsafe(::glPopMatrix()); +} + bool GLGizmoCut3D::can_perform_cut() const { BoundingBoxf3 box = bounding_box(); @@ -684,46 +867,125 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); - if (0.0 < object_cut_z && can_perform_cut()) + if (0.0 < object_cut_z && can_perform_cut()) { wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); + m_selected.clear(); + } else { // the object is SLA-elevated and the plane is under it. } } -bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) + + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) { - if (is_dragging()) + if (!m_c->raycaster()->raycaster()) return false; - const ModelObject *mo = m_c->selection_info()->model_object(); - const ModelInstance *mi = mo->instances[m_c->selection_info()->get_active_instance()]; - const Transform3d instance_trafo = mi->get_transformation().get_matrix(); + + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Transform3d instance_trafo = mi->get_transformation().get_matrix(); const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); const Camera& camera = wxGetApp().plater()->get_camera(); int mesh_id = -1; - for (const ModelVolume *mv : mo->volumes) { + for (const ModelVolume* mv : mo->volumes) { ++mesh_id; - if (! mv->is_model_part()) + if (!mv->is_model_part()) continue; Vec3f hit; Vec3f normal; bool clipping_plane_was_hit = false; m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), - camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), - nullptr, &clipping_plane_was_hit); + camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { - // The clipping plane was clicked, hit containts coordinates of the hit in world coords. - std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); return true; } } return false; } +void GLGizmoCut3D::reset_connectors() +{ + m_c->selection_info()->model_object()->cut_connectors.clear(); + m_selected.clear(); +} + +void GLGizmoCut3D::update_connector_shape() +{ + if (m_connector_shape.is_initialized()) + m_connector_shape.reset(); + + bool is_prizm = m_connector_style == size_t(Prizm); + const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; + + + switch (ConnectorShape(m_connector_shape_id)) { + case Triangle: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 3))); + break; + case Square: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 4))); + break; + case Circle: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, 2 * PI / 360)); + break; + case Hexagon: + m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 6))); + break; + } +} + +bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (is_dragging() || action != SLAGizmoEventType::LeftDown) + return false; + + ModelObject *mo = m_c->selection_info()->model_object(); + const Camera& camera = wxGetApp().plater()->get_camera(); + + int mesh_id = -1; + + // left down without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + std::pair pos_and_normal; + if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { + const Vec3f& hit = pos_and_normal.first; + const Vec3f& normal = pos_and_normal.second; + // The clipping plane was clicked, hit containts coordinates of the hit in world coords. + std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin")); + + mo->cut_connectors.emplace_back(hit, -normal, float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + m_selected.push_back(false); + assert(m_selected.size() == mo->cut_connectors.size()); + m_parent.set_as_dirty(); + m_wait_for_up_event = true; + + return true; + } + return false; + } + return true; + } + return false; +} + CommonGizmosDataID GLGizmoCut3D::on_get_requirements() const { return CommonGizmosDataID( int(CommonGizmosDataID::SelectionInfo) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 995d1f82d..e116a6df1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -18,6 +18,7 @@ class GLGizmoCut3D : public GLGizmoBase { GLGizmoRotate3D m_rotation_gizmo; double m_snap_step{ 1.0 }; + int m_connectors_group_id; Vec3d m_plane_center{ Vec3d::Zero() }; // data to check position of the cut palne center on gizmo activation @@ -26,6 +27,7 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; + GLModel m_connector_shape; #if ENABLE_GLBEGIN_GLEND_REMOVAL GLModel m_plane; @@ -39,14 +41,18 @@ class GLGizmoCut3D : public GLGizmoBase bool m_hide_cut_plane{ false }; - double m_connector_depth_ratio{ 1.5 }; - double m_connector_size{ 5.0 }; + double m_connector_depth_ratio{ 5.0 }; + double m_connector_size{ 2.0 }; float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; bool m_imperial_units{ false }; bool suppress_update_clipper_on_render{false}; + mutable std::vector m_selected; // which pins are currently selected + bool m_selection_empty = true; + bool m_wait_for_up_event = false; + Matrix3d m_rotation_matrix; Vec3d m_rotations{ Vec3d::Zero() }; @@ -95,7 +101,7 @@ class GLGizmoCut3D : public GLGizmoBase size_t m_connector_style{ size_t(Prizm) }; std::vector m_connector_shapes; - size_t m_connector_shape{ size_t(Hexagon) }; + size_t m_connector_shape_id{ size_t(Hexagon) }; std::vector m_axis_names; @@ -103,6 +109,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; + bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); /// @@ -136,13 +143,15 @@ protected: private: void set_center(const Vec3d& center); - void render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); - void render_double_input(const std::string& label, double& value_in); + bool render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); + bool render_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(ConnectorMode mode); bool render_revert_button(const std::string& label); void render_connect_type_radio_button(ConnectorType type); + void render_connectors(bool picking); + bool can_perform_cut() const; void render_cut_plane(); @@ -150,6 +159,8 @@ private: void perform_cut(const Selection& selection); void set_center_pos(const Vec3d& center_pos); bool update_bb(); + void reset_connectors(); + void update_connector_shape(); }; } // namespace GUI From 01aa99f67fc0c11f31879b3d48b2e7e242e798b1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 14 Mar 2022 14:09:10 +0100 Subject: [PATCH 14/93] After merge fixes --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 147 +++++++++++++++------------ src/slic3r/GUI/MeshUtils.cpp | 41 +++++--- 2 files changed, 111 insertions(+), 77 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5ba119685..0e3dd3067 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -360,34 +360,33 @@ void GLGizmoCut3D::render_cut_plane() if (shader == nullptr) return; shader->start_using(); -/* const Vec3d diff = plane_center - m_old_center; - // Z changed when move with cut plane - // X and Y changed when move with cutted object - bool is_changed = std::abs(diff.x()) > EPSILON || - std::abs(diff.y()) > EPSILON || - std::abs(diff.z()) > EPSILON; - m_old_center = plane_center; -*/ +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( + m_plane_center, + m_rotation_gizmo.get_rotation(), + Vec3d::Ones(), + Vec3d::Ones() + ); + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#else const Vec3d& angles = m_rotation_gizmo.get_rotation(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; - init_data.reserve_vertices(4); - init_data.reserve_indices(6); - glsafe(::glPushMatrix()); glsafe(::glTranslated(m_plane_center.x(), m_plane_center.y(), m_plane_center.z())); glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (!m_plane.is_initialized()) { m_plane.reset(); - // indices - init_data.add_triangle(0, 1, 2); - init_data.add_triangle(2, 3, 0); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); // vertices init_data.add_vertex(Vec3f(min_x, min_y, 0.0)); @@ -395,14 +394,17 @@ void GLGizmoCut3D::render_cut_plane() init_data.add_vertex(Vec3f(max_x, max_y, 0.0)); init_data.add_vertex(Vec3f(min_x, max_y, 0.0)); -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix()); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); + + m_plane.init_from(std::move(init_data)); + } m_plane.render(); +#if !ENABLE_GL_SHADERS_ATTRIBUTES glsafe(::glPopMatrix()); +#endif //!ENABLE_GL_SHADERS_ATTRIBUTES #else // Draw the cutting plane ::glBegin(GL_QUADS); @@ -437,33 +439,41 @@ void GLGizmoCut3D::render_cut_center_graber() glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glLineWidth(m_hover_id == m_group_id ? 2.0f : 1.5f)); +#if ENABLE_GL_SHADERS_ATTRIBUTES + GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); +#else GLShaderProgram* shader = wxGetApp().get_shader("flat"); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader != nullptr) { shader->start_using(); - // if (!m_grabber_connection.is_initialized() || z_changed) - { - m_grabber_connection.reset(); +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_grabber_connection.reset(); - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA::YELLOW(); - init_data.reserve_vertices(2); - init_data.reserve_indices(2); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::YELLOW(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); - // vertices - init_data.add_vertex((Vec3f)m_plane_center.cast()); - init_data.add_vertex((Vec3f)m_grabbers[0].center.cast()); + // vertices + init_data.add_vertex((Vec3f)m_plane_center.cast()); + init_data.add_vertex((Vec3f)m_grabbers[0].center.cast()); - // indices - init_data.add_line(0, 1); + // indices + init_data.add_line(0, 1); + + m_grabber_connection.init_from(std::move(init_data)); - m_grabber_connection.init_from(std::move(init_data)); - } m_grabber_connection.render(); shader->stop_using(); } +#if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES shader = wxGetApp().get_shader("gouraud_light_attr"); #else @@ -565,7 +575,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) for (auto& connector : connectors) connector.pos += shift; } -#endif // ENABLE_LEGACY_OPENGL_REMOVAL + else if (m_hover_id > m_group_id) { std::pair pos_and_normal; @@ -693,7 +703,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) revert_rotation = render_revert_button("rotation"); for (Axis axis : {X, Y, Z}) render_rotation_input(axis); - m_imgui->text(_L("°")); + m_imgui->text(_L("°")); } else { ImGui::AlignTextToFramePadding(); @@ -792,27 +802,31 @@ void GLGizmoCut3D::render_connectors(bool picking) { const Selection& selection = m_parent.get_selection(); -#if ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_GL_SHADERS_ATTRIBUTES + GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr"); +#else GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader == nullptr) return; shader->start_using(); + ScopeGuard guard([shader]() { shader->stop_using(); }); #else GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); if (shader) shader->start_using(); ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL - - const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); - //const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); - //const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); +#else glsafe(::glPushMatrix()); glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); -// glsafe(::glMultMatrixd(instance_matrix.data())); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES ColorRGBA render_color; const CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; @@ -832,41 +846,44 @@ void GLGizmoCut3D::render_connectors(bool picking) render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); } -#if ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_LEGACY_OPENGL_REMOVAL m_connector_shape.set_color(render_color); #else const_cast(&m_connector_shape)->set_color(-1, render_color); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL - // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( + Vec3d(connector.pos.x(), connector.pos.y(), connector.pos.z() - 0.5 * connector.height), + m_rotation_gizmo.get_rotation(), + Vec3d(connector.radius, connector.radius, connector.height), + Vec3d::Ones() + ); + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#else glsafe(::glPushMatrix()); -// glsafe(::glTranslatef(connector.pos.x() - m_plane_center.x(), connector.pos.y() - m_plane_center.y(), connector.pos.z() - m_plane_center.z())); glsafe(::glTranslatef(connector.pos.x(), connector.pos.y(), connector.pos.z())); -// glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); - - if (vol->is_left_handed()) - glFrontFace(GL_CW); const Vec3d& angles = m_rotation_gizmo.get_rotation(); glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - // Matrices set, we can render the point mark now. - /* Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), instance_scaling_matrix_inverse * (-connector.normal).cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis().x(), aa.axis().y(), aa.axis().z())); -*/ glsafe(::glTranslated(0., 0., -0.5*connector.height)); + glsafe(::glTranslated(0., 0., -0.5*connector.height)); glsafe(::glScaled(connector.radius, connector.radius, connector.height)); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_connector_shape.render(); - if (vol->is_left_handed()) - glFrontFace(GL_CCW); +#if !ENABLE_GL_SHADERS_ATTRIBUTES glsafe(::glPopMatrix()); +#endif //!ENABLE_GL_SHADERS_ATTRIBUTES } +#if !ENABLE_GL_SHADERS_ATTRIBUTES glsafe(::glPopMatrix()); +#endif //!ENABLE_GL_SHADERS_ATTRIBUTES } bool GLGizmoCut3D::can_perform_cut() const @@ -916,10 +933,12 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair if (!m_c->raycaster()->raycaster()) return false; + const float sla_shift = m_c->selection_info()->get_sla_shift(); + const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; - const Transform3d instance_trafo = mi->get_transformation().get_matrix(); - const Transform3d instance_trafo_not_translate = mi->get_transformation().get_matrix(true); + const Transform3d instance_trafo = sla_shift > 0.0 ? + Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift), Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones()) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); int mesh_id = -1; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 604339bf0..e5cd1f79e 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -119,22 +119,32 @@ void MeshClipper::render_cut() } -#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_LEGACY_OPENGL_REMOVAL void MeshClipper::render_contour(const ColorRGBA& color) #else void MeshClipper::render_contour() -#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#endif // ENABLE_LEGACY_OPENGL_REMOVAL { if (! m_triangles_valid) recalculate_triangles(); -#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); if (curr_shader != nullptr) curr_shader->stop_using(); +#if ENABLE_GL_SHADERS_ATTRIBUTES + GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); +#else GLShaderProgram* shader = wxGetApp().get_shader("flat"); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + if (shader != nullptr) { shader->start_using(); +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES m_model_expanded.set_color(color); m_model_expanded.render(); shader->stop_using(); @@ -145,7 +155,7 @@ void MeshClipper::render_contour() #else if (m_vertex_array_expanded.has_VBOs()) m_vertex_array_expanded.render(); -#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#endif // ENABLE_LEGACY_OPENGL_REMOVAL } @@ -232,13 +242,17 @@ void MeshClipper::recalculate_triangles() tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting + std::vector triangles2d = m_fill_cut + ? triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.) + : std::vector(); + #if ENABLE_LEGACY_OPENGL_REMOVAL m_model.reset(); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(m_triangles2d.size()); - init_data.reserve_indices(m_triangles2d.size()); + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); // vertices + indices for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { @@ -272,11 +286,11 @@ void MeshClipper::recalculate_triangles() } -#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_LEGACY_OPENGL_REMOVAL m_model_expanded.reset(); init_data = GLModel::Geometry(); - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(triangles2d.size()) }; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(triangles2d.size()); init_data.reserve_indices(triangles2d.size()); @@ -286,10 +300,11 @@ void MeshClipper::recalculate_triangles() init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); const size_t idx = it - triangles2d.cbegin(); - if (init_data.format.index_type == GLModel::Geometry::EIndexType::USHORT) - init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); - else - init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + //if (init_data./*format.*/index_type == GLModel::Geometry::EIndexType::USHORT) + // init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + //else + // init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); } if (!init_data.is_empty()) @@ -304,7 +319,7 @@ void MeshClipper::recalculate_triangles() m_vertex_array_expanded.push_triangle(idx, idx+1, idx+2); } m_vertex_array_expanded.finalize_geometry(true); -#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#endif // ENABLE_LEGACY_OPENGL_REMOVAL From b204f05809f929d46cf5bb4f85cb94cc6a75f7f4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 14 Mar 2022 16:54:50 +0100 Subject: [PATCH 15/93] Cut: ObjectList: Show info about added cut connectors. + Some code refactoring: Put CutConnectorsType, CutConnectorsStyle and CutConnectorsShape to the Model.hpp. --- src/libslic3r/Model.hpp | 23 ++++++++++- src/slic3r/GUI/GUI_ObjectList.cpp | 16 +++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 56 ++++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 41 ++++++------------- src/slic3r/GUI/ObjectDataViewModel.cpp | 1 + src/slic3r/GUI/ObjectDataViewModel.hpp | 1 + 6 files changed, 89 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2e96910f4..c0e7e907d 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -257,8 +257,6 @@ struct CutConnector static constexpr size_t steps = 32; }; -using CutConnectors = std::vector; - // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -269,6 +267,27 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; +using CutConnectors = std::vector; + +enum class CutConnectorType : int { + Plug + , Dowel +}; + +enum class CutConnectorStyle : int { + Prizm + , Frustrum + //,Claw +}; + +enum class CutConnectorShape : int { + Triangle + , Square + , Hexagon + , Circle + //,D-shape +}; + enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 27c374b3f..7462e8153 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1841,6 +1841,12 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type) mv->seam_facets.reset(); break; + case InfoItemType::Cut: + cnv->get_gizmos_manager().reset_all_states(); + Plater::TakeSnapshot(plater, _L("Remove cut connectors")); + (*m_objects)[obj_idx]->cut_connectors.clear(); + break; + case InfoItemType::MmuSegmentation: cnv->get_gizmos_manager().reset_all_states(); Plater::TakeSnapshot(plater, _L("Remove Multi Material painting")); @@ -2464,10 +2470,12 @@ void ObjectList::part_selection_changed() } case InfoItemType::CustomSupports: case InfoItemType::CustomSeam: + case InfoItemType::Cut: case InfoItemType::MmuSegmentation: { - GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports : - info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam : + GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports : + info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam : + info_type == InfoItemType::Cut ? GLGizmosManager::EType::Cut : GLGizmosManager::EType::MmuSegmentation; GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); if (gizmos_mgr.get_current_type() != gizmo_type) @@ -2604,6 +2612,7 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam, + InfoItemType::Cut, InfoItemType::MmuSegmentation, InfoItemType::Sinking, InfoItemType::VariableLayerHeight}) { @@ -2624,6 +2633,9 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio }); break; + case InfoItemType::Cut : + should_show = !model_object->cut_connectors.empty(); + break; case InfoItemType::VariableLayerHeight : should_show = printer_technology() == ptFFF && ! model_object->layer_height_profile.empty(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 0e3dd3067..9c5074a45 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -26,6 +26,9 @@ static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) + , m_connector_type (CutConnectorType::Plug) + , m_connector_style (size_t(CutConnectorStyle::Prizm)) + , m_connector_shape_id (size_t(CutConnectorShape::Hexagon)) , m_rotation_gizmo(GLGizmoRotate3D(parent, "", -1)) , m_rotation_matrix( Eigen::AngleAxisd(0.0, Vec3d::UnitZ()) * Eigen::AngleAxisd(0.0, Vec3d::UnitY()) @@ -46,7 +49,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, // , _u8L("Claw") }; - m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Circle"), _u8L("Hexagon") + m_connector_shapes = { _u8L("Triangle"), _u8L("Square"), _u8L("Hexagon"), _u8L("Circle") // , _u8L("D-shape") }; @@ -295,17 +298,17 @@ void GLGizmoCut3D::render_rotation_input(int axis) } } -void GLGizmoCut3D::render_connect_type_radio_button(ConnectorType type) +void GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type) { - ImGui::SameLine(type == ConnectorType::Plug ? m_label_width : 2*m_label_width); + ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 2*m_label_width); ImGui::PushItemWidth(m_control_width); if (m_imgui->radio_button(m_connector_types[int(type)], m_connector_type == type)) m_connector_type = type; } -void GLGizmoCut3D::render_connect_mode_radio_button(ConnectorMode mode) +void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode) { - ImGui::SameLine(mode == ConnectorMode::Auto ? m_label_width : 2*m_label_width); + ImGui::SameLine(mode == CutConnectorMode::Auto ? m_label_width : 2*m_label_width); ImGui::PushItemWidth(m_control_width); if (m_imgui->radio_button(m_connector_modes[int(mode)], m_connector_mode == mode)) m_connector_mode = mode; @@ -517,8 +520,16 @@ std::string GLGizmoCut3D::on_get_name() const void GLGizmoCut3D::on_set_state() { - if (get_state() == On) + if (get_state() == On) { update_bb(); + + m_selected.clear(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { + const CutConnectors& connectors = selection->model_object()->cut_connectors; + for (size_t i = 0; i < connectors.size(); ++i) + m_selected.push_back(false); + } + } m_rotation_gizmo.set_center(m_plane_center); m_rotation_gizmo.set_state(m_state); @@ -738,12 +749,12 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); m_imgui->text(_L("Mode")); - render_connect_mode_radio_button(ConnectorMode::Auto); - render_connect_mode_radio_button(ConnectorMode::Manual); + render_connect_mode_radio_button(CutConnectorMode::Auto); + render_connect_mode_radio_button(CutConnectorMode::Manual); m_imgui->text(_L("Type")); - render_connect_type_radio_button(ConnectorType::Plug); - render_connect_type_radio_button(ConnectorType::Dowel); + render_connect_type_radio_button(CutConnectorType::Plug); + render_connect_type_radio_button(CutConnectorType::Dowel); if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) update_connector_shape(); @@ -854,7 +865,7 @@ void GLGizmoCut3D::render_connectors(bool picking) #if ENABLE_GL_SHADERS_ATTRIBUTES const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( - Vec3d(connector.pos.x(), connector.pos.y(), connector.pos.z() - 0.5 * connector.height), + Vec3d(connector.pos.x(), connector.pos.y(), connector.pos.z()), m_rotation_gizmo.get_rotation(), Vec3d(connector.radius, connector.radius, connector.height), Vec3d::Ones() @@ -964,6 +975,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair void GLGizmoCut3D::reset_connectors() { m_c->selection_info()->model_object()->cut_connectors.clear(); + update_model_object(); m_selected.clear(); } @@ -972,26 +984,35 @@ void GLGizmoCut3D::update_connector_shape() if (m_connector_shape.is_initialized()) m_connector_shape.reset(); - bool is_prizm = m_connector_style == size_t(Prizm); + bool is_prizm = m_connector_style == size_t(CutConnectorStyle::Prizm); const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; - switch (ConnectorShape(m_connector_shape_id)) { - case Triangle: + switch (CutConnectorShape(m_connector_shape_id)) { + case CutConnectorShape::Triangle: m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 3))); break; - case Square: + case CutConnectorShape::Square: m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 4))); break; - case Circle: + case CutConnectorShape::Circle: m_connector_shape.init_from(its_make_shape(1.0, 1.0, 2 * PI / 360)); break; - case Hexagon: + case CutConnectorShape::Hexagon: m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 6))); break; } } +void GLGizmoCut3D::update_model_object() const +{ + const ModelObjectPtrs& mos = wxGetApp().model().objects; + ModelObject* mo = m_c->selection_info()->model_object(); + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (is_dragging() || action != SLAGizmoEventType::LeftDown) @@ -1019,6 +1040,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin")); mo->cut_connectors.emplace_back(hit, -normal, float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + update_model_object(); m_selected.push_back(false); assert(m_selected.size() == mo->cut_connectors.size()); m_parent.set_as_dirty(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 8394dba60..1fedcd22e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -9,6 +9,9 @@ #include "libslic3r/ObjectID.hpp" namespace Slic3r { + +enum class CutConnectorType : int; + namespace GUI { class Selection; @@ -41,8 +44,8 @@ class GLGizmoCut3D : public GLGizmoBase bool m_hide_cut_plane{ false }; - double m_connector_depth_ratio{ 5.0 }; - double m_connector_size{ 2.0 }; + double m_connector_depth_ratio{ 3.0 }; + double m_connector_size{ 2.5 }; float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; @@ -64,44 +67,25 @@ class GLGizmoCut3D : public GLGizmoBase //,cutModular }; - enum ConnectorMode { + enum CutConnectorMode { Auto , Manual }; - enum ConnectorType { - Plug - , Dowel - }; - - enum ConnectorStyle { - Prizm - , Frustrum - //,Claw - }; - - enum ConnectorShape { - Triangle - , Square - , Circle - , Hexagon - //,D-shape - }; - std::vector m_modes; size_t m_mode{ size_t(cutPlanar) }; std::vector m_connector_modes; - ConnectorMode m_connector_mode{ Auto }; + CutConnectorMode m_connector_mode{ Auto }; std::vector m_connector_types; - ConnectorType m_connector_type{ Plug }; + CutConnectorType m_connector_type; std::vector m_connector_styles; - size_t m_connector_style{ size_t(Prizm) }; + size_t m_connector_style; std::vector m_connector_shapes; - size_t m_connector_shape_id{ size_t(Hexagon) }; + size_t m_connector_shape_id; std::vector m_axis_names; @@ -147,9 +131,9 @@ private: bool render_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); void render_rotation_input(int axis); - void render_connect_mode_radio_button(ConnectorMode mode); + void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); - void render_connect_type_radio_button(ConnectorType type); + void render_connect_type_radio_button(CutConnectorType type); void render_connectors(bool picking); bool can_perform_cut() const; @@ -161,6 +145,7 @@ private: bool update_bb(); void reset_connectors(); void update_connector_shape(); + void update_model_object() const; }; } // namespace GUI diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 496cdcfc7..908307125 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -48,6 +48,7 @@ const std::map INFO_ITEMS{ // info_item Type info_item Name info_item BitmapName { InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports_" }, }, { InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, }, + { InfoItemType::Cut, {L("Cut connectors"), "cut_" }, }, { InfoItemType::MmuSegmentation, {L("Multimaterial painting"), "mmu_segmentation_"}, }, { InfoItemType::Sinking, {L("Sinking"), "sinking"}, }, { InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers"}, }, diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index f8885b206..8669a8fd0 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -51,6 +51,7 @@ enum class InfoItemType Undef, CustomSupports, CustomSeam, + Cut, MmuSegmentation, Sinking, VariableLayerHeight From 09249e3b8dfc2ecb7172903bac49c6ad1731a1bf Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 15 Mar 2022 17:08:15 +0100 Subject: [PATCH 16/93] Cut: Perform cut with connectors --- src/libslic3r/Model.cpp | 77 +++++++++++++++++++++++++--- src/libslic3r/Model.hpp | 55 +++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 40 +++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 4 files changed, 139 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 8d44f3e7c..5e018a212 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1336,6 +1336,53 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr return res; } +void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) +{ + if (cut_connectors.empty()) + return; + + bool is_prizm = connector_attributes.style == CutConnectorStyle::Prizm; + const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; + + indexed_triangle_set connector_mesh; + switch (CutConnectorShape(connector_attributes.shape)) { + case CutConnectorShape::Triangle: + connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 3)); + break; + case CutConnectorShape::Square: + connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 4)); + break; + case CutConnectorShape::Circle: + connector_mesh = its_make_shape(1.0, 1.0, 2 * PI / 360); + break; + case CutConnectorShape::Hexagon: + connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 6)); + break; + } + + size_t connector_id = 0; + + for (const CutConnector& connector : cut_connectors) { + TriangleMesh mesh = TriangleMesh(connector_mesh); + // Mesh will be centered when loading. + ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); + + // Transform the new modifier to be aligned inside the instance + new_volume->set_transformation(Geometry::assemble_transform( + connector.pos, + connector.rotation, + Vec3d(connector.radius, connector.radius, connector.height), + Vec3d::Ones() + )); + + new_volume->name = name + "-" + std::to_string(++connector_id); + new_volume->source.is_connector = true; + } + + // delete all connectors + cut_connectors.clear(); +} + ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) { if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) @@ -1405,15 +1452,31 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (!volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. + // But if this modifier is a connector, then just set volume transformation + if (volume->source.is_connector) + volume->set_transformation(Geometry::Transformation(volume_matrix)); + else + volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower->add_volume(*volume); + ModelVolume* vol = { nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume* vol = upper->add_volume(*volume); + if (volume->source.is_connector) + vol->source.is_connector = true; + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume* vol = lower->add_volume(*volume); + if (volume->source.is_connector) { + vol->source.is_connector = true; + // for lower part change type of conector from NEGATIVE_VOLUME to MODEL_PART + if (vol->type() == ModelVolumeType::NEGATIVE_VOLUME) + vol->set_type(ModelVolumeType::MODEL_PART); + } + } } - else if (!volume->mesh().empty()) { + else if (!volume->mesh().empty() && + !volume->source.is_connector // we don't allow to cut a connectors + ) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c0e7e907d..dbaded841 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -221,22 +221,22 @@ private: struct CutConnector { - Vec3f pos; - Vec3f normal; + Vec3d pos; + Vec3d rotation; float radius; float height; bool failed = false; CutConnector() - : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) + : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f) {} - CutConnector(Vec3f p, Vec3f n, float r, float h, bool fl = false) - : pos(p), normal(n), radius(r), height(h), failed(fl) + CutConnector(Vec3d p, Vec3d n, float r, float h, bool fl = false) + : pos(p), rotation(n), radius(r), height(h), failed(fl) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {} + CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.failed) {} bool operator==(const CutConnector& sp) const; @@ -251,22 +251,12 @@ struct CutConnector */ template inline void serialize(Archive& ar) { - ar(pos, normal, radius, height, failed); + ar(pos, rotation, radius, height, failed); } static constexpr size_t steps = 32; }; -// Declared outside of ModelVolume, so it could be forward declared. -enum class ModelVolumeType : int { - INVALID = -1, - MODEL_PART = 0, - NEGATIVE_VOLUME, - PARAMETER_MODIFIER, - SUPPORT_BLOCKER, - SUPPORT_ENFORCER, -}; - using CutConnectors = std::vector; enum class CutConnectorType : int { @@ -288,6 +278,32 @@ enum class CutConnectorShape : int { //,D-shape }; +struct CutConnectorAttributes +{ + CutConnectorType type{ CutConnectorType::Plug }; + CutConnectorStyle style{ CutConnectorStyle::Prizm}; + CutConnectorShape shape{CutConnectorShape::Circle}; + + CutConnectorAttributes() {} + + CutConnectorAttributes(CutConnectorType t, CutConnectorStyle st, CutConnectorShape sh) + : type(t), style(st), shape(sh) + {} + + CutConnectorAttributes(const CutConnectorAttributes& rhs) : + CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} +}; + +// Declared outside of ModelVolume, so it could be forward declared. +enum class ModelVolumeType : int { + INVALID = -1, + MODEL_PART = 0, + NEGATIVE_VOLUME, + PARAMETER_MODIFIER, + SUPPORT_BLOCKER, + SUPPORT_ENFORCER, +}; + enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); @@ -416,6 +432,7 @@ public: size_t facets_count() const; size_t parts_count() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); + void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); void merge(); @@ -671,10 +688,12 @@ public: bool is_converted_from_meters{ false }; bool is_from_builtin_objects{ false }; + bool is_connector{ false }; + template void serialize(Archive& ar) { //FIXME Vojtech: Serialize / deserialize only if the Source is set. // likely testing input_file or object_idx would be sufficient. - ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects); + ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects, is_connector); } }; Source source; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9c5074a45..5448dd1a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -578,22 +578,22 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) if (wxGetKeyState(WXK_SHIFT)) projection = m_snap_step * (double)std::round(projection / m_snap_step); + Vec3d shift = starting_vec * projection; + // move cut plane center - set_center(starting_box_center + starting_vec * projection); + set_center(starting_box_center + shift); // move connectors - Vec3f shift = Vec3f(starting_vec.cast() * projection); for (auto& connector : connectors) connector.pos += shift; } else if (m_hover_id > m_group_id) { - std::pair pos_and_normal; + std::pair pos_and_normal; if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) return; connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; - connectors[m_hover_id - m_connectors_group_id].normal = -pos_and_normal.second; } } @@ -924,6 +924,28 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); if (0.0 < object_cut_z && can_perform_cut()) { + ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; + // update connectors pos as offset of its center before cut performing + if (!mo->cut_connectors.empty()) { + const std::string name = _u8L("Connector"); + for (CutConnector& connector : mo->cut_connectors) { + connector.rotation = m_rotation_gizmo.get_rotation(); + + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d norm = m_grabbers[0].center - m_plane_center; + norm.normalize(); + Vec3d shift = norm * (0.5 * connector.height); + + // culculate offset of the connector pos regarding to the instance offset and possible SLA elevation + Vec3d connector_offset = connector.pos - instance_offset; + connector_offset[Z] -= first_glvolume->get_sla_shift_z(); + + // Update connector pos. It will be used as a center of created modifiers + connector.pos = connector_offset + shift; + } + mo->apply_cut_connectors(name, CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); + } + wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | @@ -939,7 +961,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. -bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) { if (!m_c->raycaster()->raycaster()) return false; @@ -965,7 +987,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); + pos_and_normal = std::make_pair(hit.cast(), normal.cast()); return true; } } @@ -1031,10 +1053,10 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi // If there is some selection, don't add new point and deselect everything instead. if (m_selection_empty) { - std::pair pos_and_normal; + std::pair pos_and_normal; if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { - const Vec3f& hit = pos_and_normal.first; - const Vec3f& normal = pos_and_normal.second; + const Vec3d& hit = pos_and_normal.first; + const Vec3d& normal = pos_and_normal.second; // The clipping plane was clicked, hit containts coordinates of the hit in world coords. std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 1fedcd22e..33d254c0d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -93,7 +93,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; - bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); /// From e785a66a014cfd4173ce0e5498e989fab0e234d1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 17 Mar 2022 17:35:41 +0100 Subject: [PATCH 17/93] Cut: Added possibility to delete a selected connector + Save connector position in object's local coordinates + Added missed cut_.svg --- resources/icons/cut_.svg | 28 +++++++++++ src/libslic3r/Model.cpp | 4 +- src/libslic3r/Model.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 75 ++++++++++++++++------------ 4 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 resources/icons/cut_.svg diff --git a/resources/icons/cut_.svg b/resources/icons/cut_.svg new file mode 100644 index 000000000..a7f462bb9 --- /dev/null +++ b/resources/icons/cut_.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e018a212..dcb4c503a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1474,8 +1474,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const } } } - else if (!volume->mesh().empty() && - !volume->source.is_connector // we don't allow to cut a connectors + else if (!volume->mesh().empty() +// && !volume->source.is_connector // we don't allow to cut a connectors ) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index dbaded841..c0b2e8e39 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -231,8 +231,8 @@ struct CutConnector : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f) {} - CutConnector(Vec3d p, Vec3d n, float r, float h, bool fl = false) - : pos(p), rotation(n), radius(r), height(h), failed(fl) + CutConnector(Vec3d p, Vec3d rot, float r, float h, bool fl = false) + : pos(p), rotation(rot), radius(r), height(h), failed(fl) {} CutConnector(const CutConnector& rhs) : diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5448dd1a2..ea82d61e2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -30,9 +30,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_style (size_t(CutConnectorStyle::Prizm)) , m_connector_shape_id (size_t(CutConnectorShape::Hexagon)) , m_rotation_gizmo(GLGizmoRotate3D(parent, "", -1)) - , m_rotation_matrix( Eigen::AngleAxisd(0.0, Vec3d::UnitZ()) - * Eigen::AngleAxisd(0.0, Vec3d::UnitY()) - * Eigen::AngleAxisd(0.0, Vec3d::UnitX())) + , m_rotation_matrix(Slic3r::Matrix3d::Identity()) { m_rotation_gizmo.use_only_grabbers(); m_group_id = 3; @@ -518,16 +516,14 @@ std::string GLGizmoCut3D::on_get_name() const return _u8L("Cut"); } -void GLGizmoCut3D::on_set_state() +void GLGizmoCut3D::on_set_state() { if (get_state() == On) { update_bb(); - m_selected.clear(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { - const CutConnectors& connectors = selection->model_object()->cut_connectors; - for (size_t i = 0; i < connectors.size(); ++i) - m_selected.push_back(false); + m_selected.clear(); + m_selected.resize(selection->model_object()->cut_connectors.size(), false); } } m_rotation_gizmo.set_center(m_plane_center); @@ -811,8 +807,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoCut3D::render_connectors(bool picking) { - const Selection& selection = m_parent.get_selection(); - #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr"); @@ -840,7 +834,8 @@ void GLGizmoCut3D::render_connectors(bool picking) #endif // ENABLE_GL_SHADERS_ATTRIBUTES ColorRGBA render_color; - const CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + const ModelObject* mo = m_c->selection_info()->model_object(); + const CutConnectors& connectors = mo->cut_connectors; size_t cache_size = connectors.size(); for (size_t i = 0; i < cache_size; ++i) { @@ -863,9 +858,14 @@ void GLGizmoCut3D::render_connectors(bool picking) const_cast(&m_connector_shape)->set_color(-1, render_color); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + // recalculate connector position to world position + Vec3d pos = connector.pos; + pos += mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + pos[Z] += m_c->selection_info()->get_sla_shift(); + #if ENABLE_GL_SHADERS_ATTRIBUTES const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( - Vec3d(connector.pos.x(), connector.pos.y(), connector.pos.z()), + Vec3d(pos.x(), pos.y(), pos.z()), m_rotation_gizmo.get_rotation(), Vec3d(connector.radius, connector.radius, connector.height), Vec3d::Ones() @@ -874,7 +874,7 @@ void GLGizmoCut3D::render_connectors(bool picking) shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #else glsafe(::glPushMatrix()); - glsafe(::glTranslatef(connector.pos.x(), connector.pos.y(), connector.pos.z())); + glsafe(::glTranslatef(pos.x(), pos.y(), pos.z())); const Vec3d& angles = m_rotation_gizmo.get_rotation(); glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); @@ -927,7 +927,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; // update connectors pos as offset of its center before cut performing if (!mo->cut_connectors.empty()) { - const std::string name = _u8L("Connector"); for (CutConnector& connector : mo->cut_connectors) { connector.rotation = m_rotation_gizmo.get_rotation(); @@ -935,15 +934,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d norm = m_grabbers[0].center - m_plane_center; norm.normalize(); Vec3d shift = norm * (0.5 * connector.height); - - // culculate offset of the connector pos regarding to the instance offset and possible SLA elevation - Vec3d connector_offset = connector.pos - instance_offset; - connector_offset[Z] -= first_glvolume->get_sla_shift_z(); - - // Update connector pos. It will be used as a center of created modifiers - connector.pos = connector_offset + shift; + connector.pos += shift; } - mo->apply_cut_connectors(name, CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); + mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); } wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), @@ -963,9 +956,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // Return false if no intersection was found, true otherwise. bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) { - if (!m_c->raycaster()->raycaster()) - return false; - const float sla_shift = m_c->selection_info()->get_sla_shift(); const ModelObject* mo = m_c->selection_info()->model_object(); @@ -986,8 +976,13 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { + // recalculate hit to object's local position + Vec3d hit_d = hit.cast(); + hit_d -= mi->get_offset(); + hit_d[Z] -= sla_shift; + // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit.cast(), normal.cast()); + pos_and_normal = std::make_pair(hit_d, normal.cast()); return true; } } @@ -1037,15 +1032,15 @@ void GLGizmoCut3D::update_model_object() const bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { - if (is_dragging() || action != SLAGizmoEventType::LeftDown) + if (is_dragging()) return false; - ModelObject *mo = m_c->selection_info()->model_object(); + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; const Camera& camera = wxGetApp().plater()->get_camera(); int mesh_id = -1; - // left down without selection rectangle - place point on the mesh: + // left down without selection rectangle - place connector on the cut plane: if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id != -1) @@ -1059,12 +1054,12 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi const Vec3d& normal = pos_and_normal.second; // The clipping plane was clicked, hit containts coordinates of the hit in world coords. std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add pin")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector")); - mo->cut_connectors.emplace_back(hit, -normal, float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + connectors.emplace_back(hit, m_rotation_gizmo.get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); update_model_object(); m_selected.push_back(false); - assert(m_selected.size() == mo->cut_connectors.size()); + assert(m_selected.size() == connectors.size()); m_parent.set_as_dirty(); m_wait_for_up_event = true; @@ -1074,6 +1069,22 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi } return true; } + else if (action == SLAGizmoEventType::RightDown && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id < m_connectors_group_id) + return false; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector")); + + size_t connector_id = m_hover_id - m_connectors_group_id; + connectors.erase(connectors.begin() + connector_id); + update_model_object(); + m_selected.erase(m_selected.begin() + connector_id); + assert(m_selected.size() == connectors.size()); + m_parent.set_as_dirty(); + + return true; + } return false; } From 861187997bf989c6cef5aff866564bcaba7ce8f4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 18 Mar 2022 11:31:10 +0100 Subject: [PATCH 18/93] Cut: Pt connectors to the cut plane --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 40 ++++++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index ea82d61e2..511622cf2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -160,6 +160,30 @@ void GLGizmoCut3D::rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, c vec += center; } +void GLGizmoCut3D::put_connetors_on_cut_plane() +{ + ModelObject* mo = m_c->selection_info()->model_object(); + if (CutConnectors& connectors = mo->cut_connectors; !connectors.empty()) { + const float sla_shift = m_c->selection_info()->get_sla_shift(); + const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + + const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); + const Vec3d& normal = cp->get_normal(); + + for (auto& connector : connectors) { + // convert connetor pos to the world coordinates + Vec3d pos = connector.pos + instance_offset; + pos[Z] += sla_shift; + + // scalar distance from point to plane along the normal + double distance = cp->distance(pos); + + // move connector + connector.pos += distance * normal; + } + } +} + void GLGizmoCut3D::update_clipper() { const Vec3d& angles = m_rotation_gizmo.get_rotation(); @@ -177,6 +201,8 @@ void GLGizmoCut3D::update_clipper() double dist = (m_plane_center - beg).norm(); m_c->object_clipper()->set_range_and_pos(beg, end, dist); + + put_connetors_on_cut_plane(); } void GLGizmoCut3D::update_clipper_on_render() @@ -578,10 +604,6 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) // move cut plane center set_center(starting_box_center + shift); - - // move connectors - for (auto& connector : connectors) - connector.pos += shift; } else if (m_hover_id > m_group_id) @@ -838,6 +860,9 @@ void GLGizmoCut3D::render_connectors(bool picking) const CutConnectors& connectors = mo->cut_connectors; size_t cache_size = connectors.size(); + const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + const float sla_shift = m_c->selection_info()->get_sla_shift(); + for (size_t i = 0; i < cache_size; ++i) { const CutConnector& connector = connectors[i]; const bool& point_selected = m_selected[i]; @@ -859,9 +884,8 @@ void GLGizmoCut3D::render_connectors(bool picking) #endif // ENABLE_GLBEGIN_GLEND_REMOVAL // recalculate connector position to world position - Vec3d pos = connector.pos; - pos += mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); - pos[Z] += m_c->selection_info()->get_sla_shift(); + Vec3d pos = connector.pos + instance_offset; + pos[Z] += sla_shift; #if ENABLE_GL_SHADERS_ATTRIBUTES const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( @@ -918,7 +942,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin()); const double object_cut_z = m_plane_center.z() - first_glvolume->get_sla_shift_z(); - Vec3d instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); + const Vec3d& instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 33d254c0d..a9552e5e7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -105,6 +105,7 @@ public: void shift_cut_z(double delta); void rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center); + void put_connetors_on_cut_plane(); void update_clipper(); void update_clipper_on_render(); From 301d0d5288d46f6328fd805e4104fde55a08eb15 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 22 Mar 2022 11:25:48 +0100 Subject: [PATCH 19/93] Cut WIP: * Processed Auto/Manual connetor's mode * Processed Dowel type of connectors * Added TriangeMesh::its_make_frustum_dowel --- src/libslic3r/Model.cpp | 128 +++++++++++++++++++-------- src/libslic3r/Model.hpp | 3 +- src/libslic3r/TriangleMesh.cpp | 55 ++++++++++++ src/libslic3r/TriangleMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 92 +++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 8 +- 6 files changed, 206 insertions(+), 81 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index dcb4c503a..00c489ac1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -712,6 +712,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t ModelVolume* v = new ModelVolume(this, other); if (type != ModelVolumeType::INVALID && v->type() != type) v->set_type(type); + v->source.is_connector = other.source.is_connector; this->volumes.push_back(v); // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. // v->center_geometry_after_creation(); @@ -1336,29 +1337,42 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttr return res; } +indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes) +{ + indexed_triangle_set connector_mesh; + + int sectorCount; + switch (CutConnectorShape(connector_attributes.shape)) { + case CutConnectorShape::Triangle: + sectorCount = 3; + break; + case CutConnectorShape::Square: + sectorCount = 4; + break; + case CutConnectorShape::Circle: + sectorCount = 360; + break; + case CutConnectorShape::Hexagon: + sectorCount = 6; + break; + } + + if (connector_attributes.style == CutConnectorStyle::Prizm) + connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount)); + else if (connector_attributes.type == CutConnectorType::Plug) + connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount)); + else + connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount); + + return connector_mesh; +} + void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) { if (cut_connectors.empty()) return; - bool is_prizm = connector_attributes.style == CutConnectorStyle::Prizm; - const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; - - indexed_triangle_set connector_mesh; - switch (CutConnectorShape(connector_attributes.shape)) { - case CutConnectorShape::Triangle: - connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 3)); - break; - case CutConnectorShape::Square: - connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 4)); - break; - case CutConnectorShape::Circle: - connector_mesh = its_make_shape(1.0, 1.0, 2 * PI / 360); - break; - case CutConnectorShape::Hexagon: - connector_mesh = its_make_shape(1.0, 1.0, (2 * PI / 6)); - break; - } + indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes); size_t connector_id = 0; @@ -1393,6 +1407,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // Clone the object to duplicate instances, materials etc. ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr; ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr; + ModelObject* dowels = attributes.has(ModelObjectCutAttribute::CreateDowels) ? ModelObject::new_clone(*this) : nullptr; if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { upper->set_model(nullptr); @@ -1412,6 +1427,15 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const lower->input_file.clear(); } + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + dowels->set_model(nullptr); + dowels->sla_support_points.clear(); + dowels->sla_drain_holes.clear(); + dowels->sla_points_status = sla::PointsStatus::NoPoints; + dowels->clear_volumes(); + dowels->input_file.clear(); + } + // Because transformations are going to be applied to meshes directly, // we reset transformation of all instances and volumes, // except for translation and Z-rotation on instances, which are preserved @@ -1450,29 +1474,31 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const volume->mmu_segmentation_facets.reset(); if (!volume->is_model_part()) { - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - // But if this modifier is a connector, then just set volume transformation - if (volume->source.is_connector) - volume->set_transformation(Geometry::Transformation(volume_matrix)); - else - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - - ModelVolume* vol = { nullptr }; - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume* vol = upper->add_volume(*volume); - if (volume->source.is_connector) - vol->source.is_connector = true; - } - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - ModelVolume* vol = lower->add_volume(*volume); - if (volume->source.is_connector) { - vol->source.is_connector = true; - // for lower part change type of conector from NEGATIVE_VOLUME to MODEL_PART - if (vol->type() == ModelVolumeType::NEGATIVE_VOLUME) + if (volume->source.is_connector) { + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + ModelVolume* vol = upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume* vol = lower->add_volume(*volume); + if (!attributes.has(ModelObjectCutAttribute::CreateDowels)) + // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + // add one more solid part same as connector if this connector is a dowel + // But discard rotation and Z-offset for this volume + volume->set_rotation(Vec3d::Zero()); + Vec3d offset = volume->get_offset(); + offset[Z] = 0.0; + volume->set_offset(offset); + + ModelVolume* vol = dowels->add_volume(*volume); + vol->set_type(ModelVolumeType::MODEL_PART); + } } + else + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); } else if (!volume->mesh().empty() // && !volume->source.is_connector // we don't allow to cut a connectors @@ -1584,6 +1610,32 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const res.push_back(lower); } + if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels->volumes.size() > 0) { + if (!dowels->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { + dowels->center_around_origin(); + dowels->translate_instances(-dowels->origin_translation); + dowels->origin_translation = Vec3d::Zero(); + } + else { + dowels->invalidate_bounding_box(); + dowels->center_around_origin(); + } + + dowels->name += "-Dowels"; + + // Reset instance transformation except offset and Z-rotation + for (size_t i = 0; i < instances.size(); ++i) { + auto& obj_instance = dowels->instances[i]; + const Vec3d offset = obj_instance->get_offset(); + const double rot_z = obj_instance->get_rotation().z(); + obj_instance->set_transformation(Geometry::Transformation()); + obj_instance->set_offset(offset); + obj_instance->set_rotation(Vec3d(0.0, 0.0, i == instance ? 0.0 : rot_z)); + } + + res.push_back(dowels); + } + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; return res; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c0b2e8e39..c8ae063ec 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -304,7 +304,7 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower }; +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower, CreateDowels }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); @@ -432,6 +432,7 @@ public: size_t facets_count() const; size_t parts_count() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); + static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 0dffdaab0..f98d7d4dc 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1053,6 +1053,61 @@ indexed_triangle_set its_make_sphere(double radius, double fa) return mesh; } +// Generates mesh for a frustum dowel centered about the origin, using the count of sectors +// Note: This function uses code for sphere generation, but for stackCount = 2; +indexed_triangle_set its_make_frustum_dowel(double radius, double h, int sectorCount) +{ + int stackCount = 2; + float sectorStep = float(2. * M_PI / sectorCount); + float stackStep = float(M_PI / stackCount); + + indexed_triangle_set mesh; + auto& vertices = mesh.vertices; + vertices.reserve((stackCount - 1) * sectorCount + 2); + for (int i = 0; i <= stackCount; ++i) { + // from pi/2 to -pi/2 + double stackAngle = 0.5 * M_PI - stackStep * i; + double xy = radius * cos(stackAngle); + double z = radius * sin(stackAngle); + if (i == 0 || i == stackCount) + vertices.emplace_back(Vec3f(float(xy), 0.f, float(h * sin(stackAngle)))); + else + for (int j = 0; j < sectorCount; ++j) { + // from 0 to 2pi + double sectorAngle = sectorStep * j; + vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast()); + } + } + + auto& facets = mesh.indices; + facets.reserve(2 * (stackCount - 1) * sectorCount); + for (int i = 0; i < stackCount; ++i) { + // Beginning of current stack. + int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount); + int k1_first = k1; + // Beginning of next stack. + int k2 = (i == 0) ? 1 : (k1 + sectorCount); + int k2_first = k2; + for (int j = 0; j < sectorCount; ++j) { + // 2 triangles per sector excluding first and last stacks + int k1_next = k1; + int k2_next = k2; + if (i != 0) { + k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1); + facets.emplace_back(k1, k2, k1_next); + } + if (i + 1 != stackCount) { + k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1); + facets.emplace_back(k1_next, k2, k2_next); + } + k1 = k1_next; + k2 = k2_next; + } + } + + return mesh; +} + indexed_triangle_set its_convex_hull(const std::vector &pts) { std::vector dst_vertices; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 3f3af0261..abf9cefb7 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -302,6 +302,7 @@ indexed_triangle_set its_make_cube(double x, double y, double z); indexed_triangle_set its_make_prism(float width, float length, float height); indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360)); +indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 511622cf2..411cebbb5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -326,8 +326,10 @@ void GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type) { ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 2*m_label_width); ImGui::PushItemWidth(m_control_width); - if (m_imgui->radio_button(m_connector_types[int(type)], m_connector_type == type)) + if (m_imgui->radio_button(m_connector_types[size_t(type)], m_connector_type == type)) { m_connector_type = type; + update_connector_shape(); + } } void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode) @@ -606,7 +608,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) set_center(starting_box_center + shift); } - else if (m_hover_id > m_group_id) + else if (m_hover_id > m_group_id && m_connector_mode == CutConnectorMode::Manual) { std::pair pos_and_normal; if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) @@ -669,7 +671,7 @@ void GLGizmoCut3D::on_render() if (!m_hide_cut_plane) { render_cut_plane(); render_cut_center_graber(); - if (m_mode == CutMode::cutPlanar) { + if (m_mode == size_t(CutMode::cutPlanar)) { if (m_hover_id < m_group_id) m_rotation_gizmo.render(); } @@ -716,10 +718,12 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool revert_rotation{ false }; bool revert_move{ false }; - if (m_mode <= CutMode::cutByLine) { + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + + if (m_mode <= size_t(CutMode::cutByLine)) { ImGui::Separator(); - if (m_mode == CutMode::cutPlanar) { + if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Move center")); revert_move = render_revert_button("move"); @@ -748,11 +752,18 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::AlignTextToFramePadding(); m_imgui->text(_L("After cut")); ImGui::SameLine(m_label_width); - m_imgui->checkbox(_L("Keep upper part"), m_keep_upper); + + m_imgui->disabled_begin(!connectors.empty()); + + bool keep = true; + m_imgui->checkbox(_L("Keep upper part"), connectors.empty() ? m_keep_upper : keep); m_imgui->text(""); ImGui::SameLine(m_label_width); - m_imgui->checkbox(_L("Keep lower part"), m_keep_lower); + m_imgui->checkbox(_L("Keep lower part"), connectors.empty() ? m_keep_lower : keep); m_imgui->text(""); + + m_imgui->disabled_end(); + ImGui::SameLine(m_label_width); m_imgui->disabled_begin(!m_keep_lower); m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); @@ -779,7 +790,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) update_connector_shape(); - CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) for (auto& connector : connectors) connector.height = float(m_connector_depth_ratio); @@ -829,6 +839,9 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoCut3D::render_connectors(bool picking) { + if (m_connector_mode == CutConnectorMode::Auto) + return; + #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr"); @@ -863,6 +876,9 @@ void GLGizmoCut3D::render_connectors(bool picking) const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); const float sla_shift = m_c->selection_info()->get_sla_shift(); + const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); + const Vec3d& normal = cp ? cp->get_normal() : Vec3d::Ones(); + for (size_t i = 0; i < cache_size; ++i) { const CutConnector& connector = connectors[i]; const bool& point_selected = m_selected[i]; @@ -883,15 +899,21 @@ void GLGizmoCut3D::render_connectors(bool picking) const_cast(&m_connector_shape)->set_color(-1, render_color); #endif // ENABLE_GLBEGIN_GLEND_REMOVAL + double height = connector.height; // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; + if (m_connector_type == CutConnectorType::Dowel && + m_connector_style == size_t(CutConnectorStyle::Prizm)) { + pos -= height * normal; + height *= 2; + } pos[Z] += sla_shift; #if ENABLE_GL_SHADERS_ATTRIBUTES const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( Vec3d(pos.x(), pos.y(), pos.z()), m_rotation_gizmo.get_rotation(), - Vec3d(connector.radius, connector.radius, connector.height), + Vec3d(connector.radius, connector.radius, height), Vec3d::Ones() ); shader->set_uniform("view_model_matrix", view_model_matrix); @@ -906,7 +928,7 @@ void GLGizmoCut3D::render_connectors(bool picking) glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); glsafe(::glTranslated(0., 0., -0.5*connector.height)); - glsafe(::glScaled(connector.radius, connector.radius, connector.height)); + glsafe(::glScaled(connector.radius, connector.radius, height)); #endif // ENABLE_GL_SHADERS_ATTRIBUTES m_connector_shape.render(); @@ -947,26 +969,37 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); + bool create_dowels_as_separate_object = false; if (0.0 < object_cut_z && can_perform_cut()) { ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; + const bool has_connectors = !mo->cut_connectors.empty(); // update connectors pos as offset of its center before cut performing - if (!mo->cut_connectors.empty()) { + if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { for (CutConnector& connector : mo->cut_connectors) { connector.rotation = m_rotation_gizmo.get_rotation(); - // culculate shift of the connector center regarding to the position on the cut plane - Vec3d norm = m_grabbers[0].center - m_plane_center; - norm.normalize(); - Vec3d shift = norm * (0.5 * connector.height); - connector.pos += shift; + if (m_connector_style == size_t(CutConnectorStyle::Prizm)) { + if (m_connector_type == CutConnectorType::Dowel) + connector.height *= 2; + else { + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d norm = m_grabbers[0].center - m_plane_center; + norm.normalize(); + Vec3d shift = norm * (0.5 * connector.height); + connector.pos += shift; + } + } } mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); + if (m_connector_type == CutConnectorType::Dowel) + create_dowels_as_separate_object = true; } wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), - only_if(m_keep_upper, ModelObjectCutAttribute::KeepUpper) | - only_if(m_keep_lower, ModelObjectCutAttribute::KeepLower) | - only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower)); + only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); m_selected.clear(); } else { @@ -1025,24 +1058,7 @@ void GLGizmoCut3D::update_connector_shape() if (m_connector_shape.is_initialized()) m_connector_shape.reset(); - bool is_prizm = m_connector_style == size_t(CutConnectorStyle::Prizm); - const std::function& its_make_shape = is_prizm ? its_make_cylinder : its_make_cone; - - - switch (CutConnectorShape(m_connector_shape_id)) { - case CutConnectorShape::Triangle: - m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 3))); - break; - case CutConnectorShape::Square: - m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 4))); - break; - case CutConnectorShape::Circle: - m_connector_shape.init_from(its_make_shape(1.0, 1.0, 2 * PI / 360)); - break; - case CutConnectorShape::Hexagon: - m_connector_shape.init_from(its_make_shape(1.0, 1.0, (2 * PI / 6))); - break; - } + m_connector_shape.init_from(ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) })); } void GLGizmoCut3D::update_model_object() const @@ -1056,7 +1072,7 @@ void GLGizmoCut3D::update_model_object() const bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { - if (is_dragging()) + if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) return false; CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index a9552e5e7..57be3c739 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -59,7 +59,7 @@ class GLGizmoCut3D : public GLGizmoBase Matrix3d m_rotation_matrix; Vec3d m_rotations{ Vec3d::Zero() }; - enum CutMode { + enum class CutMode { cutPlanar , cutByLine , cutGrig @@ -67,16 +67,16 @@ class GLGizmoCut3D : public GLGizmoBase //,cutModular }; - enum CutConnectorMode { + enum class CutConnectorMode { Auto , Manual }; std::vector m_modes; - size_t m_mode{ size_t(cutPlanar) }; + size_t m_mode{ size_t(CutMode::cutPlanar) }; std::vector m_connector_modes; - CutConnectorMode m_connector_mode{ Auto }; + CutConnectorMode m_connector_mode{ CutConnectorMode::Manual }; std::vector m_connector_types; CutConnectorType m_connector_type; From fdaca50d4b8843bc9b2240e1091d7b1395701cd4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 23 Mar 2022 09:26:15 +0100 Subject: [PATCH 20/93] Cut WIP: Implemented flip of the upper part after performing of the cut --- src/libslic3r/Model.cpp | 18 +++++-- src/libslic3r/Model.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 71 ++++++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 4 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 00c489ac1..ed341dfc6 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1465,6 +1465,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); + Vec3d local_dowels_displace = Vec3d::Zero(); for (ModelVolume* volume : volumes) { const auto volume_matrix = volume->get_matrix(); @@ -1493,6 +1494,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const ModelVolume* vol = dowels->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); + + // Compute the displacement (in instance coordinates) to be applied to place the dowels + local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); } } else @@ -1580,8 +1584,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const obj_instance->set_transformation(Geometry::Transformation()); obj_instance->set_offset(offset + displace); - if (i != instance) - obj_instance->set_rotation(Vec3d(0.0, 0.0, rot_z)); + obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipUpper) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z)); } res.push_back(upper); @@ -1627,10 +1630,15 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const for (size_t i = 0; i < instances.size(); ++i) { auto& obj_instance = dowels->instances[i]; const Vec3d offset = obj_instance->get_offset(); - const double rot_z = obj_instance->get_rotation().z(); + Vec3d rotation = Vec3d::Zero(); + if (i != instance) + rotation[Z] = obj_instance->get_rotation().z(); + + const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), rotation) * local_dowels_displace; + obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset); - obj_instance->set_rotation(Vec3d(0.0, 0.0, i == instance ? 0.0 : rot_z)); + obj_instance->set_offset(offset + displace); + obj_instance->set_rotation(rotation); } res.push_back(dowels); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c8ae063ec..b3c5b47db 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -266,7 +266,7 @@ enum class CutConnectorType : int { enum class CutConnectorStyle : int { Prizm - , Frustrum + , Frustum //,Claw }; @@ -304,7 +304,7 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipLower, CreateDowels }; +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, CreateDowels }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 411cebbb5..be040e729 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -43,7 +43,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, m_connector_modes = { _u8L("Auto"), _u8L("Manual") }; m_connector_types = { _u8L("Plug"), _u8L("Dowel") }; - m_connector_styles = { _u8L("Prizm"), _u8L("Frustrum") + m_connector_styles = { _u8L("Prizm"), _u8L("Frustum") // , _u8L("Claw") }; @@ -750,24 +750,54 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut")); + m_imgui->text(_L("After cut") + ": "); + bool keep = true; + ImGui::SameLine(m_label_width); + m_imgui->text(_L("Upper part")); + ImGui::SameLine(2*m_label_width); m_imgui->disabled_begin(!connectors.empty()); - - bool keep = true; - m_imgui->checkbox(_L("Keep upper part"), connectors.empty() ? m_keep_upper : keep); - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->checkbox(_L("Keep lower part"), connectors.empty() ? m_keep_lower : keep); - m_imgui->text(""); - + m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(!m_keep_upper); + m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper); m_imgui->disabled_end(); + m_imgui->text(""); ImGui::SameLine(m_label_width); + m_imgui->text(_L("Lower part")); + ImGui::SameLine(2*m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); + m_imgui->disabled_end(); + ImGui::SameLine(); m_imgui->disabled_begin(!m_keep_lower); - m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); + m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower); m_imgui->disabled_end(); + + //m_imgui->disabled_begin(!connectors.empty()); + + //bool keep = true; + //m_imgui->checkbox(_L("Keep upper part"), connectors.empty() ? m_keep_upper : keep); + //m_imgui->text(""); + //ImGui::SameLine(m_label_width); + //m_imgui->checkbox(_L("Keep lower part"), connectors.empty() ? m_keep_lower : keep); + + //m_imgui->disabled_end(); + + //m_imgui->disabled_begin(!m_keep_upper); + //m_imgui->text(""); + //ImGui::SameLine(m_label_width); + //m_imgui->checkbox(_L("Rotate upper part upwards"), m_rotate_upper); + //m_imgui->disabled_end(); + //m_imgui->disabled_begin(!m_keep_lower); + //m_imgui->text(""); + //ImGui::SameLine(m_label_width); + //m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); + //m_imgui->disabled_end(); } m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); @@ -978,16 +1008,16 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) for (CutConnector& connector : mo->cut_connectors) { connector.rotation = m_rotation_gizmo.get_rotation(); - if (m_connector_style == size_t(CutConnectorStyle::Prizm)) { - if (m_connector_type == CutConnectorType::Dowel) + if (m_connector_type == CutConnectorType::Dowel) { + if (m_connector_style == size_t(CutConnectorStyle::Prizm)) connector.height *= 2; - else { - // culculate shift of the connector center regarding to the position on the cut plane - Vec3d norm = m_grabbers[0].center - m_plane_center; - norm.normalize(); - Vec3d shift = norm * (0.5 * connector.height); - connector.pos += shift; - } + } + else { + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d norm = m_grabbers[0].center - m_plane_center; + norm.normalize(); + Vec3d shift = norm * (0.5 * connector.height); + connector.pos += shift; } } mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); @@ -998,6 +1028,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); m_selected.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 57be3c739..99bc66d5e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -40,6 +40,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_keep_upper{ true }; bool m_keep_lower{ true }; + bool m_rotate_upper{ false }; bool m_rotate_lower{ false }; bool m_hide_cut_plane{ false }; From a42212487d618efc2d84287b0bc230395de30dc7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 23 Mar 2022 16:01:31 +0100 Subject: [PATCH 21/93] Cut WIP: * rewrite ObjectClipper::set_range_and_pos(). A calculation of a normal and distance is extracted from this function and are used for a putting of connectors to the cut plane. * Some replacement of items of the CutGizmo window --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 121 +++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 7 ++ src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 1 + 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index be040e729..a56218860 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -160,26 +160,21 @@ void GLGizmoCut3D::rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, c vec += center; } -void GLGizmoCut3D::put_connetors_on_cut_plane() +void GLGizmoCut3D::put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset) { ModelObject* mo = m_c->selection_info()->model_object(); if (CutConnectors& connectors = mo->cut_connectors; !connectors.empty()) { const float sla_shift = m_c->selection_info()->get_sla_shift(); const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); - const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); - const Vec3d& normal = cp->get_normal(); - for (auto& connector : connectors) { // convert connetor pos to the world coordinates Vec3d pos = connector.pos + instance_offset; pos[Z] += sla_shift; - // scalar distance from point to plane along the normal - double distance = cp->distance(pos); - + double distance = -cp_normal.dot(pos) + cp_offset; // move connector - connector.pos += distance * normal; + connector.pos += distance * cp_normal; } } } @@ -200,9 +195,15 @@ void GLGizmoCut3D::update_clipper() double dist = (m_plane_center - beg).norm(); - m_c->object_clipper()->set_range_and_pos(beg, end, dist); + // calculate normal and offset for clipping plane + Vec3d normal = end - beg; + dist = std::clamp(dist, 0.0001, normal.norm()); + normal.normalize(); + const double offset = normal.dot(beg) + dist; - put_connetors_on_cut_plane(); + m_c->object_clipper()->set_range_and_pos(normal, offset, dist); + + put_connetors_on_cut_plane(normal, offset); } void GLGizmoCut3D::update_clipper_on_render() @@ -726,14 +727,22 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Move center")); + + m_imgui->disabled_begin(m_plane_center == bounding_box().center()); revert_move = render_revert_button("move"); + m_imgui->disabled_end(); + for (Axis axis : {X, Y, Z}) render_move_center_input(axis); m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Rotation")); + + m_imgui->disabled_begin(m_rotation_gizmo.get_rotation() == Vec3d::Zero()); revert_rotation = render_revert_button("rotation"); + m_imgui->disabled_end(); + for (Axis axis : {X, Y, Z}) render_rotation_input(axis); m_imgui->text(_L("°")); @@ -748,56 +757,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding(); } - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut") + ": "); - bool keep = true; - - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Upper part")); - ImGui::SameLine(2*m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); - m_imgui->disabled_end(); - ImGui::SameLine(); - m_imgui->disabled_begin(!m_keep_upper); - m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper); - m_imgui->disabled_end(); - - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Lower part")); - ImGui::SameLine(2*m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); - m_imgui->disabled_end(); - ImGui::SameLine(); - m_imgui->disabled_begin(!m_keep_lower); - m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower); - m_imgui->disabled_end(); - - //m_imgui->disabled_begin(!connectors.empty()); - - //bool keep = true; - //m_imgui->checkbox(_L("Keep upper part"), connectors.empty() ? m_keep_upper : keep); - //m_imgui->text(""); - //ImGui::SameLine(m_label_width); - //m_imgui->checkbox(_L("Keep lower part"), connectors.empty() ? m_keep_lower : keep); - - //m_imgui->disabled_end(); - - //m_imgui->disabled_begin(!m_keep_upper); - //m_imgui->text(""); - //ImGui::SameLine(m_label_width); - //m_imgui->checkbox(_L("Rotate upper part upwards"), m_rotate_upper); - //m_imgui->disabled_end(); - //m_imgui->disabled_begin(!m_keep_lower); - //m_imgui->text(""); - //ImGui::SameLine(m_label_width); - //m_imgui->checkbox(_L("Rotate lower part upwards"), m_rotate_lower); - //m_imgui->disabled_end(); } m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); @@ -807,6 +766,12 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::AlignTextToFramePadding(); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); + m_imgui->disabled_begin(connectors.empty()); + ImGui::SameLine(m_label_width); + if (m_imgui->button(" " + _L("Reset") + " ##connectors")) + reset_connectors(); + m_imgui->disabled_end(); + m_imgui->text(_L("Mode")); render_connect_mode_radio_button(CutConnectorMode::Auto); render_connect_mode_radio_button(CutConnectorMode::Manual); @@ -827,12 +792,40 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) for (auto& connector : connectors) connector.radius = float(m_connector_size * 0.5); - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut()); - if (m_imgui->button(_L("Reset connectors"))) - reset_connectors(); m_imgui->disabled_end(); - m_imgui->disabled_end(); + if (m_mode <= size_t(CutMode::cutByLine)) { + ImGui::Separator(); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("After cut") + ": "); + bool keep = true; + + ImGui::SameLine(m_label_width); + m_imgui->text(_L("Upper part")); + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(!m_keep_upper); + m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper); + m_imgui->disabled_end(); + + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->text(_L("Lower part")); + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(!m_keep_lower); + m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower); + m_imgui->disabled_end(); + } ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 99bc66d5e..9fc43ec2a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -106,7 +106,7 @@ public: void shift_cut_z(double delta); void rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center); - void put_connetors_on_cut_plane(); + void put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset); void update_clipper(); void update_clipper_on_render(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index a6008b183..26003085e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -476,6 +476,13 @@ void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, dou get_pool()->get_canvas()->set_as_dirty(); } +void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos) +{ + m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset)); + m_clp_ratio = pos; + get_pool()->get_canvas()->set_as_dirty(); +} + const ClippingPlane* ObjectClipper::get_clipping_plane() const { static const ClippingPlane no_clip = ClippingPlane::ClipsNothing(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index c5c238408..226e5f7b5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -261,6 +261,7 @@ public: void render_cut() const; void set_position_by_ratio(double pos, bool keep_normal); void set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos); + void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); From f9e22513c10b3b5c120551ddea0a19a34601a6be Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 30 Mar 2022 11:48:22 +0200 Subject: [PATCH 22/93] Cut WIP: * Added a first detection if a connector position is valid * Code cleaning: Deleted unused set_range_and_pos function * Some code refactoring --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 148 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 11 -- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 1 - src/slic3r/GUI/MeshUtils.cpp | 11 ++ src/slic3r/GUI/MeshUtils.hpp | 2 + 6 files changed, 124 insertions(+), 58 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index a56218860..5ee74cc49 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -369,18 +369,6 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id) void GLGizmoCut3D::render_cut_plane() { - const BoundingBoxf3 box = bounding_box(); - - const float min_x = box.min.x() - Margin - m_plane_center.x(); - const float max_x = box.max.x() + Margin - m_plane_center.x(); - const float min_y = box.min.y() - Margin - m_plane_center.y(); - const float max_y = box.max.y() + Margin - m_plane_center.y(); - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_CULL_FACE)); - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); @@ -389,6 +377,12 @@ void GLGizmoCut3D::render_cut_plane() #endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader == nullptr) return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + shader->start_using(); #if ENABLE_GL_SHADERS_ATTRIBUTES const Camera& camera = wxGetApp().plater()->get_camera(); @@ -410,14 +404,18 @@ void GLGizmoCut3D::render_cut_plane() #endif // ENABLE_GL_SHADERS_ATTRIBUTES if (!m_plane.is_initialized()) { - m_plane.reset(); - GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; init_data.reserve_vertices(4); init_data.reserve_indices(6); + const BoundingBoxf3 bb = bounding_box(); + const float min_x = bb.min.x() - Margin - m_plane_center.x(); + const float max_x = bb.max.x() + Margin - m_plane_center.x(); + const float min_y = bb.min.y() - Margin - m_plane_center.y(); + const float max_y = bb.max.y() + Margin - m_plane_center.y(); + // vertices init_data.add_vertex(Vec3f(min_x, min_y, 0.0)); init_data.add_vertex(Vec3f(max_x, min_y, 0.0)); @@ -547,14 +545,9 @@ std::string GLGizmoCut3D::on_get_name() const void GLGizmoCut3D::on_set_state() { - if (get_state() == On) { + if (get_state() == On) update_bb(); - if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { - m_selected.clear(); - m_selected.resize(selection->model_object()->cut_connectors.size(), false); - } - } m_rotation_gizmo.set_center(m_plane_center); m_rotation_gizmo.set_state(m_state); @@ -653,6 +646,13 @@ bool GLGizmoCut3D::update_bb() m_min_pos = box.min; m_bb_center = box.center(); set_center_pos(m_bb_center + m_center_offset); + + m_plane.reset(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { + m_selected.clear(); + m_selected.resize(selection->model_object()->cut_connectors.size(), false); + } + return true; } return false; @@ -841,7 +841,8 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) static bool hide_clipped = true; static bool fill_cut = true; static float contour_width = 0.2f; - m_imgui->checkbox("hide_clipped", hide_clipped); + if (m_imgui->checkbox("hide_clipped", hide_clipped) && !hide_clipped) + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_imgui->checkbox("fill_cut", fill_cut); m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); m_c->object_clipper()->set_behavior(hide_clipped, fill_cut, contour_width); @@ -860,11 +861,43 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } } +// get volume transformation regarding to the "border". Border is related from the siae of connectors +Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const +{ + bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm); + const Transform3d connector_trafo = Geometry::assemble_transform( + is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(), + m_rotation_gizmo.get_rotation(), + Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio), + Vec3d::Ones()); + const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size(); + + const Vec3d bb = volume->mesh().bounding_box().size(); + + // calculate an unused border - part of the the volume, where we can't put connectors + const Vec3d border_scale(connector_bb.x() / bb.x(), connector_bb.y() / bb.y(), connector_bb.z() / bb.z()); + + const Transform3d vol_matrix = volume->get_matrix(); + const Vec3d vol_trans = vol_matrix.translation(); + // offset of the volume will be changed after scaling, so calculate the needed offset and set it to a volume_trafo + const Vec3d offset(vol_trans.x() * border_scale.x(), vol_trans.y() * border_scale.y(), vol_trans.z() * border_scale.z()); + + // scale and translate volume to suppress to put connectors too close to the border + return Geometry::assemble_transform(offset, Vec3d::Zero(), Vec3d::Ones() - border_scale, Vec3d::Ones()) * vol_matrix; +} + void GLGizmoCut3D::render_connectors(bool picking) { + m_has_invalid_connector = false; + if (m_connector_mode == CutConnectorMode::Auto) return; + const ModelObject* mo = m_c->selection_info()->model_object(); + const CutConnectors& connectors = mo->cut_connectors; + if (connectors.size() != m_selected.size()) + return; + #if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_GL_SHADERS_ATTRIBUTES GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr"); @@ -892,47 +925,59 @@ void GLGizmoCut3D::render_connectors(bool picking) #endif // ENABLE_GL_SHADERS_ATTRIBUTES ColorRGBA render_color; - const ModelObject* mo = m_c->selection_info()->model_object(); - const CutConnectors& connectors = mo->cut_connectors; - size_t cache_size = connectors.size(); - const Vec3d& instance_offset = mo->instances[m_c->selection_info()->get_active_instance()]->get_offset(); + const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const Vec3d& instance_offset = mi->get_offset(); const float sla_shift = m_c->selection_info()->get_sla_shift(); const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); - const Vec3d& normal = cp ? cp->get_normal() : Vec3d::Ones(); + const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; - for (size_t i = 0; i < cache_size; ++i) { + const Transform3d instance_trafo = Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift), Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones()) * mi->get_transformation().get_matrix(); + + for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; const bool& point_selected = m_selected[i]; + double height = connector.height; + // recalculate connector position to world position + Vec3d pos = connector.pos + instance_offset; + if (m_connector_type == CutConnectorType::Dowel && + m_connector_style == size_t(CutConnectorStyle::Prizm)) { + pos -= height * normal; + height *= 2; + } + pos[Z] += sla_shift; + // First decide about the color of the point. if (picking) render_color = picking_decode(BASE_ID - i - m_connectors_group_id); else { if (size_t(m_hover_id- m_connectors_group_id) == i) render_color = ColorRGBA::CYAN(); - else // neither hover nor picking - render_color = point_selected ? ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f) : ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); - } + else { // neither hover nor picking + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + ++mesh_id; + if (!mv->is_model_part()) + continue; -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_connector_shape.set_color(render_color); -#else - const_cast(&m_connector_shape)->set_color(-1, render_color); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL + const Transform3d volume_trafo = get_volume_transformation(mv); - double height = connector.height; - // recalculate connector position to world position - Vec3d pos = connector.pos + instance_offset; - if (m_connector_type == CutConnectorType::Dowel && - m_connector_style == size_t(CutConnectorStyle::Prizm)) { - pos -= height * normal; - height *= 2; + if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { + render_color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); + break; + } + render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + } + if (!m_has_invalid_connector && render_color == ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f)) + m_has_invalid_connector = true; + } } - pos[Z] += sla_shift; #if ENABLE_GL_SHADERS_ATTRIBUTES + m_connector_shape.set_color(render_color); + const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( Vec3d(pos.x(), pos.y(), pos.z()), m_rotation_gizmo.get_rotation(), @@ -942,6 +987,8 @@ void GLGizmoCut3D::render_connectors(bool picking) shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #else + const_cast(&m_connector_shape)->set_color(-1, render_color); + glsafe(::glPushMatrix()); glsafe(::glTranslatef(pos.x(), pos.y(), pos.z())); @@ -968,6 +1015,9 @@ void GLGizmoCut3D::render_connectors(bool picking) bool GLGizmoCut3D::can_perform_cut() const { + if (m_has_invalid_connector) + return false; + BoundingBoxf3 box = bounding_box(); double dist = (m_plane_center - box.center()).norm(); if (dist > box.radius()) @@ -1053,7 +1103,10 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair Vec3f hit; Vec3f normal; bool clipping_plane_was_hit = false; - m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * mv->get_matrix(), + + const Transform3d volume_trafo = get_volume_transformation(mv); + + m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * volume_trafo, camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { @@ -1082,7 +1135,12 @@ void GLGizmoCut3D::update_connector_shape() if (m_connector_shape.is_initialized()) m_connector_shape.reset(); - m_connector_shape.init_from(ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) })); + const indexed_triangle_set its = ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }); + m_connector_shape.init_from(its); + + m_connector_mesh.clear(); + m_connector_mesh = TriangleMesh(its); + } void GLGizmoCut3D::update_model_object() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 9fc43ec2a..ca49d2e4e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -11,6 +11,7 @@ namespace Slic3r { enum class CutConnectorType : int; +class ModelVolume; namespace GUI { class Selection; @@ -30,7 +31,10 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; - GLModel m_connector_shape; + GLModel m_connector_shape; + TriangleMesh m_connector_mesh; + // workaround for using of the clipping plane normal + Vec3d m_clp_normal{ Vec3d::Ones() }; #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; @@ -57,6 +61,8 @@ class GLGizmoCut3D : public GLGizmoBase bool m_selection_empty = true; bool m_wait_for_up_event = false; + bool m_has_invalid_connector{ false }; + Matrix3d m_rotation_matrix; Vec3d m_rotations{ Vec3d::Zero() }; @@ -136,6 +142,7 @@ private: void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); void render_connect_type_radio_button(CutConnectorType type); + Transform3d get_volume_transformation(const ModelVolume* volume) const; void render_connectors(bool picking); bool can_perform_cut() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 26003085e..7f60892b1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -465,17 +465,6 @@ void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) get_pool()->get_canvas()->set_as_dirty(); } -void ObjectClipper::set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos) -{ - Vec3d normal = end-origin; - double norm = normal.norm(); - pos = std::clamp(pos, 0.0001, norm); - normal.normalize(); - m_clp.reset(new ClippingPlane(normal, normal.dot(origin)+pos)); - m_clp_ratio = pos; - get_pool()->get_canvas()->set_as_dirty(); -} - void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos) { m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 226e5f7b5..efd3b436e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -260,7 +260,6 @@ public: const ClippingPlane* get_clipping_plane() const; void render_cut() const; void set_position_by_ratio(double pos, bool keep_normal); - void set_range_and_pos(const Vec3d& origin, const Vec3d& end, double pos); void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index e5cd1f79e..ca274e429 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -416,6 +416,17 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& } +bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const +{ + point = trafo.inverse() * point; + + std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); + + return !hits.empty() && !neg_hits.empty(); +} + + std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector& points, const ClippingPlane* clipping_plane) const { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index dc522eb3b..0faf07e29 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -157,6 +157,8 @@ public: bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section? ) const; + bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; + // Given a vector of points in woorld coordinates, this returns vector // of indices of points that are visible (i.e. not cut by clipping plane // or obscured by part of the mesh. From 463e9ab5300e74f1e665f5871ae446e0404c8453 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 11 Apr 2022 11:20:00 +0200 Subject: [PATCH 23/93] Cut WIP: + Added CutObjectBase class which contains cut attributes for object + ObjectList and ManipulationPanel : * Disable all ManipulationEditors for solid/negative volumes of cut object * Disable Scale/Size ManipulationEditors for objects/instances of objects which are CutParts of initial object + Scale/Rotation/Move gizmos are disabled for solid/negative volumes of cut object + Select whole CutParts of initial object when ScaleGizmo is active --- src/libslic3r/Model.cpp | 42 ++++++- src/libslic3r/Model.hpp | 6 + src/libslic3r/ObjectID.hpp | 35 ++++++ src/slic3r/GUI/GLCanvas3D.cpp | 5 + src/slic3r/GUI/GUI_ObjectList.cpp | 129 +++++++++++++++++++++- src/slic3r/GUI/GUI_ObjectList.hpp | 3 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 16 +++ src/slic3r/GUI/GUI_ObjectManipulation.hpp | 5 + src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 6 + src/slic3r/GUI/Plater.cpp | 6 +- 12 files changed, 261 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ed341dfc6..829c76fb5 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,4 +1,5 @@ #include "Model.hpp" +#include "Model.hpp" #include "libslic3r.h" #include "BuildVolume.hpp" #include "Exception.hpp" @@ -610,6 +611,8 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) this->layer_height_profile = rhs.layer_height_profile; this->printable = rhs.printable; this->origin_translation = rhs.origin_translation; + this->cut_connectors_count = rhs.cut_connectors_count; + this->cut_id.copy(rhs.cut_id); m_bounding_box = rhs.m_bounding_box; m_bounding_box_valid = rhs.m_bounding_box_valid; m_raw_bounding_box = rhs.m_raw_bounding_box; @@ -1369,12 +1372,17 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) { + // discard old connector markers vor volumes + for (ModelVolume* volume : volumes) { + volume->source.is_connector = false; + } + if (cut_connectors.empty()) return; indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes); - size_t connector_id = 0; + size_t connector_id = cut_connectors_count; for (const CutConnector& connector : cut_connectors) { TriangleMesh mesh = TriangleMesh(connector_mesh); @@ -1393,10 +1401,20 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr new_volume->source.is_connector = true; } + cut_connectors_count += cut_connectors.size(); // delete all connectors cut_connectors.clear(); } +void ModelObject::synchronize_model_after_cut() +{ + for (ModelObject* obj : m_model->objects) { + if (obj == this || obj->cut_id.is_equal(this->cut_id)) + continue; + obj->cut_id.set_check_sum(this->cut_id.check_sum()); + } +} + ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) { if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) @@ -1404,6 +1422,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; + // initiate/update cut attributes for object + if (cut_id.id().invalid()) + cut_id.init(); + { + int cut_obj_cnt = -1; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_obj_cnt++; + if (attributes.has(ModelObjectCutAttribute::KeepLower)) cut_obj_cnt++; + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) cut_obj_cnt++; + if (cut_obj_cnt > 0) + cut_id.increase_check_sum(size_t(cut_obj_cnt)); + } + // Clone the object to duplicate instances, materials etc. ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr; ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr; @@ -1499,10 +1529,16 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); } } - else + else { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower->add_volume(*volume); + } } else if (!volume->mesh().empty() // && !volume->source.is_connector // we don't allow to cut a connectors @@ -1646,6 +1682,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; + synchronize_model_after_cut(); + return res; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b3c5b47db..6fa724162 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -346,6 +346,9 @@ public: // Connectors to be added into the object after cut CutConnectors cut_connectors; + // count of connectors in object + size_t cut_connectors_count{ 0 }; + CutObjectBase cut_id; /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation @@ -434,6 +437,7 @@ public: ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); + void synchronize_model_after_cut(); ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); void merge(); @@ -458,6 +462,8 @@ public: // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) int get_repaired_errors_count(const int vol_idx = -1) const; + bool is_cut() const { return cut_id.id().valid(); } + private: friend class Model; // This constructor assigns new ID to this ModelObject and its config. diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 1030171e7..599c243b5 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -128,6 +128,41 @@ private: template void serialize(Archive &ar) { ar(m_timestamp); } }; +class CutObjectBase : public ObjectBase +{ + // check sum of CutPartsObject + size_t m_check_sum{ 1 }; + +public: + // Default Constructor to assign an invalid ID + CutObjectBase() : ObjectBase(-1) {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + CutObjectBase(int) : ObjectBase(-1) {} + // The class tree will have virtual tables and type information. + virtual ~CutObjectBase() = default; + + bool operator<(const CutObjectBase& other) const { return other.id() > this->id(); } + bool operator==(const CutObjectBase& other) const { return other.id() == this->id(); } + + void copy(const CutObjectBase& rhs) { + this->copy_id(rhs); + this->m_check_sum = rhs.check_sum(); + } + CutObjectBase operator=(const CutObjectBase& other) { + this->copy(other); + return *this; + } + + void init() { this->set_new_unique_id(); } + bool has_same_id(const CutObjectBase& rhs) { return this->id() == rhs.id(); } + bool is_equal(const CutObjectBase& rhs) { return this->id() == rhs.id() && this->check_sum() == rhs.check_sum(); } + + size_t check_sum() const { return m_check_sum; } + void set_check_sum(size_t cs) { m_check_sum = cs; } + void increase_check_sum(size_t cnt) { m_check_sum += cnt; } +}; + // Unique object / instance ID for the wipe tower. extern ObjectID wipe_tower_object_id(); extern ObjectID wipe_tower_instance_id(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 91add7c56..5fe2dab23 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3360,6 +3360,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) show_sinking_contours(); } } + else if (evt.LeftUp() && + m_gizmos.get_current_type() == GLGizmosManager::EType::Scale && + m_gizmos.get_current()->get_state() == GLGizmoBase::EState::On) { + wxGetApp().obj_list()->selection_changed(); + } #if ENABLE_OBJECT_MANIPULATOR_FOCUS handle_sidebar_focus_event("", false); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7462e8153..3cb5bf3ce 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -665,6 +665,8 @@ void ObjectList::selection_changed() fix_multiselection_conflicts(); + fix_cut_selection(); + // update object selection on Plater if (!m_prevent_canvas_selection_update) update_selections_on_canvas(); @@ -2437,6 +2439,9 @@ void ObjectList::part_selection_changed() bool update_and_show_settings = false; bool update_and_show_layers = false; + bool enable_manipulation {true}; + bool disable_ss_manipulation {false}; + const auto item = GetSelection(); if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { @@ -2445,6 +2450,43 @@ void ObjectList::part_selection_changed() const Selection& selection = scene_selection(); // don't show manipulation panel for case of all Object's parts selection update_and_show_manipulations = !selection.is_single_full_instance(); + + if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) { + if (selection.is_any_volume() || selection.is_any_modifier()) + enable_manipulation = !(*m_objects)[obj_idx]->is_cut(); + else// if (item && m_objects_model->GetItemType(item) == itInstanceRoot) + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + } + else { + wxDataViewItemArray sels; + GetSelections(sels); + if (selection.is_single_full_object() || selection.is_multiple_full_instance() ) { + int obj_idx = m_objects_model->GetObjectIdByItem(sels.front()); + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + } + else if (selection.is_mixed() || selection.is_multiple_full_object()) { + std::map> cut_objects; + + // find cut objects + for (auto item : sels) { + int obj_idx = m_objects_model->GetObjectIdByItem(item); + const ModelObject* obj = object(obj_idx); + if (obj->is_cut()) { + if (cut_objects.find(obj->cut_id) == cut_objects.end()) + cut_objects[obj->cut_id] = std::set{ obj_idx }; + else + cut_objects.at(obj->cut_id).insert(obj_idx); + } + } + + // check if selected cut objects are "full selected" + for (auto cut_object : cut_objects) + if (cut_object.first.check_sum() != cut_object.second.size()) { + disable_ss_manipulation = true; + break; + } + } + } } else { if (item) { @@ -2486,6 +2528,8 @@ void ObjectList::part_selection_changed() default: { break; } } } + else + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); } else { if (type & itSettings) { @@ -2509,6 +2553,7 @@ void ObjectList::part_selection_changed() volume_id = m_objects_model->GetVolumeIdByItem(item); m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; update_and_show_manipulations = true; + enable_manipulation = !(*m_objects)[obj_idx]->is_cut(); } else if (type & itInstance) { og_name = _L("Instance manipulation"); @@ -2516,6 +2561,7 @@ void ObjectList::part_selection_changed() // fill m_config by object's values m_config = &(*m_objects)[obj_idx]->config; + disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); } else if (type & (itLayerRoot|itLayer)) { og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range"); @@ -2538,6 +2584,11 @@ void ObjectList::part_selection_changed() wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item)); wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_info(obj_idx, volume_id)); } + + if (disable_ss_manipulation) + wxGetApp().obj_manipul()->DisableScale(); + else + wxGetApp().obj_manipul()->Enable(enable_manipulation); } if (update_and_show_settings) @@ -2554,6 +2605,7 @@ void ObjectList::part_selection_changed() panel.Freeze(); wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false); + wxGetApp().plater()->canvas3D()->enable_moving(enable_manipulation); wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations); wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings); wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers); @@ -3420,11 +3472,34 @@ void ObjectList::update_selections() if (sels.size() == 0 || m_selection_mode & smSettings) m_selection_mode = smUndef; - - select_items(sels); - // Scroll selected Item in the middle of an object list - ensure_current_item_visible(); + if (fix_cut_selection(sels)) { + m_prevent_list_events = true; + + // If some part is selected, unselect all items except of selected parts of the current object + UnselectAll(); + SetSelections(sels); + + m_prevent_list_events = false; + + // update object selection on Plater + if (!m_prevent_canvas_selection_update) + update_selections_on_canvas(); + + // to update the toolbar and info sizer + if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { + auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT); + event.SetEventObject(this); + wxPostEvent(this, event); + } + part_selection_changed(); + } + else { + select_items(sels); + + // Scroll selected Item in the middle of an object list + ensure_current_item_visible(); + } } void ObjectList::update_selections_on_canvas() @@ -3753,6 +3828,52 @@ void ObjectList::fix_multiselection_conflicts() m_prevent_list_events = false; } +bool ObjectList::fix_cut_selection(wxDataViewItemArray& sels) +{ + if (wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::Scale) { + for (const auto& item : sels) { + if (m_objects_model->GetItemType(item) & (itInstance | itObject) || + (m_objects_model->GetItemType(item) & itSettings && + m_objects_model->GetItemType(m_objects_model->GetParent(item)) & itObject)) { + + bool is_instance_selection = m_objects_model->GetItemType(item) & itInstance; + + int obj_idx = m_objects_model->GetObjectIdByItem(item); + int inst_idx = is_instance_selection ? m_objects_model->GetInstanceIdByItem(item) : 0; + + if (auto obj = object(obj_idx); obj->is_cut()) { + sels.Clear(); + + auto cut_id = obj->cut_id; + + for (int obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) { + auto object = (*m_objects)[obj_idx]; + if (object->is_cut() && object->cut_id.has_same_id(cut_id)) + sels.Add(is_instance_selection ? m_objects_model->GetItemByInstanceId(obj_idx, inst_idx) : m_objects_model->GetItemById(obj_idx)); + } + return true; + } + } + } + } + return false; +} + +void ObjectList::fix_cut_selection() +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (fix_cut_selection(sels)) { + m_prevent_list_events = true; + + // If some part is selected, unselect all items except of selected parts of the current object + UnselectAll(); + SetSelections(sels); + + m_prevent_list_events = false; + } +} + ModelVolume* ObjectList::get_selected_model_volume() { wxDataViewItem item = GetSelection(); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b9b816b7b..45072d4a5 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -353,6 +353,9 @@ public: bool check_last_selection(wxString& msg_str); // correct current selections to avoid of the possible conflicts void fix_multiselection_conflicts(); + // correct selection in respect to the cut_id if any exists + void fix_cut_selection(); + bool fix_cut_selection(wxDataViewItemArray& sels); ModelVolume* get_selected_model_volume(); void change_part_type(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 6ab87150b..089e89dfe 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -465,6 +465,22 @@ void ObjectManipulation::UpdateAndShow(const bool show) OG_Settings::UpdateAndShow(show); } +void ObjectManipulation::Enable(const bool enadle) +{ + for (auto editor : m_editors) + editor->Enable(enadle); + for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt }) + win->Enable(enadle); +} + +void ObjectManipulation::DisableScale() +{ + for (auto editor : m_editors) + editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true); + for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_lock_bnt }) + win->Enable(false); +} + void ObjectManipulation::update_ui_from_settings() { if (m_imperial_units != (wxGetApp().app_config->get("use_inches") == "1")) { diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index a15c72fb8..3a2eca2b6 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -57,6 +57,8 @@ public: void set_value(const wxString& new_value); void kill_focus(ObjectManipulation *parent); + bool has_opt_key(const std::string& key) { return m_opt_key == key; } + private: double get_value(); }; @@ -173,6 +175,9 @@ public: void Show(const bool show) override; bool IsShown() override; void UpdateAndShow(const bool show) override; + void Enable(const bool enadle = true); + void Disable() { Enable(false); } + void DisableScale(); void update_ui_from_settings(); bool use_colors() { return m_use_colors; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index fb1269d26..a172672ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -5,6 +5,7 @@ #if ENABLE_GL_SHADERS_ATTRIBUTES #include "slic3r/GUI/Plater.hpp" #endif // ENABLE_GL_SHADERS_ATTRIBUTES +#include "libslic3r/Model.hpp" #include @@ -63,7 +64,13 @@ std::string GLGizmoMove3D::on_get_name() const bool GLGizmoMove3D::on_is_activable() const { - return !m_parent.get_selection().is_empty(); + const Selection& selection = m_parent.get_selection(); + if (selection.is_any_volume() || selection.is_any_modifier()) { + if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) + return !m_parent.get_model()->objects[obj_idx]->is_cut(); + } + + return !selection.is_empty(); } void GLGizmoMove3D::on_start_dragging() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 6ab87e025..6b61befcf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -9,6 +9,7 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/Plater.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Model.hpp" #include "slic3r/GUI/Jobs/RotoptimizeJob.hpp" @@ -814,7 +815,13 @@ std::string GLGizmoRotate3D::on_get_name() const bool GLGizmoRotate3D::on_is_activable() const { - return !m_parent.get_selection().is_empty(); + const Selection& selection = m_parent.get_selection(); + if (selection.is_any_volume() || selection.is_any_modifier()) { + if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) + return !m_parent.get_model()->objects[obj_idx]->is_cut(); + } + + return !selection.is_empty(); } void GLGizmoRotate3D::on_start_dragging() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 373a2396d..be7c797be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -5,6 +5,7 @@ #if ENABLE_GL_SHADERS_ATTRIBUTES #include "slic3r/GUI/Plater.hpp" #endif // ENABLE_GL_SHADERS_ATTRIBUTES +#include "libslic3r/Model.hpp" #include @@ -136,6 +137,11 @@ std::string GLGizmoScale3D::on_get_name() const bool GLGizmoScale3D::on_is_activable() const { const Selection& selection = m_parent.get_selection(); + if (selection.is_any_volume() || selection.is_any_modifier()) { + if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) + return !m_parent.get_model()->objects[obj_idx]->is_cut(); + } + return !selection.is_empty() && !selection.is_wipe_tower(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b88cd7ce8..bff535bed 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4860,7 +4860,8 @@ bool Plater::priv::can_increase_instances() const return false; int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) + && !model.objects[obj_idx]->is_cut(); } bool Plater::priv::can_decrease_instances() const @@ -4870,7 +4871,8 @@ bool Plater::priv::can_decrease_instances() const return false; int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1) + && !model.objects[obj_idx]->is_cut(); } bool Plater::priv::can_split_to_objects() const From c29b7b1eef193c83914e26acb647c9d2b76bccd0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 12 Apr 2022 14:27:34 +0200 Subject: [PATCH 24/93] Cut WIP: * Suppress to delete/add a SolidPart/NegativeVolume from/for objects which are marked as "is cut" * Suppress to delete Instances which are marked as "is cut" * Allow delete an object which is marked as "is cut", but show warning message about break of the "cut consistency". And if this deletion was performed, the all related objects will be unmarked. * m_connectors_cnt is added into CutObjectBase class to correct synchronization of a connectors count between related objects --- src/libslic3r/Model.cpp | 17 +++++++++++------ src/libslic3r/Model.hpp | 4 ++-- src/libslic3r/ObjectID.hpp | 18 ++++++++++++++++-- src/slic3r/GUI/GUI_Factories.cpp | 9 +++++++-- src/slic3r/GUI/Plater.cpp | 26 +++++++++++++++++++++----- src/slic3r/GUI/Plater.hpp | 2 +- 6 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 829c76fb5..30dd64aa0 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1,5 +1,4 @@ #include "Model.hpp" -#include "Model.hpp" #include "libslic3r.h" #include "BuildVolume.hpp" #include "Exception.hpp" @@ -611,7 +610,6 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) this->layer_height_profile = rhs.layer_height_profile; this->printable = rhs.printable; this->origin_translation = rhs.origin_translation; - this->cut_connectors_count = rhs.cut_connectors_count; this->cut_id.copy(rhs.cut_id); m_bounding_box = rhs.m_bounding_box; m_bounding_box_valid = rhs.m_bounding_box_valid; @@ -1382,8 +1380,7 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes); - size_t connector_id = cut_connectors_count; - + size_t connector_id = cut_id.connectors_cnt(); for (const CutConnector& connector : cut_connectors) { TriangleMesh mesh = TriangleMesh(connector_mesh); // Mesh will be centered when loading. @@ -1400,18 +1397,26 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr new_volume->name = name + "-" + std::to_string(++connector_id); new_volume->source.is_connector = true; } + cut_id.increase_connectors_cnt(cut_connectors.size()); - cut_connectors_count += cut_connectors.size(); // delete all connectors cut_connectors.clear(); } +void ModelObject::invalidate_cut() +{ + for (ModelObject* obj : m_model->objects) + if (obj != this && obj->cut_id.is_equal(this->cut_id)) + obj->cut_id.ivalidate(); +} + void ModelObject::synchronize_model_after_cut() { for (ModelObject* obj : m_model->objects) { if (obj == this || obj->cut_id.is_equal(this->cut_id)) continue; - obj->cut_id.set_check_sum(this->cut_id.check_sum()); + if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id)) + obj->cut_id.copy(this->cut_id); } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 6fa724162..3afa94c75 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -346,8 +346,6 @@ public: // Connectors to be added into the object after cut CutConnectors cut_connectors; - // count of connectors in object - size_t cut_connectors_count{ 0 }; CutObjectBase cut_id; /* This vector accumulates the total translation applied to the object by the @@ -437,6 +435,8 @@ public: ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); + // invalidate cut state for this and related objects from the whole model + void invalidate_cut(); void synchronize_model_after_cut(); ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); void split(ModelObjectPtrs* new_objects); diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 599c243b5..42cd21699 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -130,8 +130,10 @@ private: class CutObjectBase : public ObjectBase { - // check sum of CutPartsObject + // check sum of CutParts in initial Object size_t m_check_sum{ 1 }; + // connectors count + size_t m_connectors_cnt{ 0 }; public: // Default Constructor to assign an invalid ID @@ -148,19 +150,31 @@ public: void copy(const CutObjectBase& rhs) { this->copy_id(rhs); this->m_check_sum = rhs.check_sum(); + this->m_connectors_cnt = rhs.connectors_cnt() ; } CutObjectBase operator=(const CutObjectBase& other) { this->copy(other); return *this; } + void ivalidate() { + set_invalid_id(); + m_check_sum = 1; + m_connectors_cnt = 0; + } + void init() { this->set_new_unique_id(); } bool has_same_id(const CutObjectBase& rhs) { return this->id() == rhs.id(); } - bool is_equal(const CutObjectBase& rhs) { return this->id() == rhs.id() && this->check_sum() == rhs.check_sum(); } + bool is_equal(const CutObjectBase& rhs) { return this->id() == rhs.id() && + this->check_sum() == rhs.check_sum() && + this->connectors_cnt() == rhs.connectors_cnt() ; } size_t check_sum() const { return m_check_sum; } void set_check_sum(size_t cs) { m_check_sum = cs; } void increase_check_sum(size_t cnt) { m_check_sum += cnt; } + + size_t connectors_cnt() const { return m_connectors_cnt; } + void increase_connectors_cnt(size_t connectors_cnt) { m_connectors_cnt += connectors_cnt; } }; // Unique object / instance ID for the wipe tower. diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 7b3476d71..d5e7260af 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -494,7 +494,9 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu) append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "", [](wxCommandEvent&) { obj_list()->load_subobject(ModelVolumeType::MODEL_PART); }, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + []() { return obj_list()->is_instance_or_object_selected() + && !obj_list()->is_selected_object_cut(); + }, m_parent); } if (mode == comSimple) { append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].first), "", @@ -515,7 +517,10 @@ void MenuFactory::append_menu_items_add_volume(wxMenu* menu) wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type)); append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second, - []() { return obj_list()->is_instance_or_object_selected(); }, m_parent); + [type]() { + bool can_add = type < size_t(ModelVolumeType::PARAMETER_MODIFIER) ? !obj_list()->is_selected_object_cut() : true; + return can_add && obj_list()->is_instance_or_object_selected(); + }, m_parent); } append_menu_item_layers_editing(menu); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bff535bed..cc577da2f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1794,7 +1794,7 @@ struct Plater::priv void select_all(); void deselect_all(); void remove(size_t obj_idx); - void delete_object_from_model(size_t obj_idx); + bool delete_object_from_model(size_t obj_idx); void delete_all_objects_from_model(); void reset(); void mirror(Axis axis); @@ -2959,16 +2959,32 @@ void Plater::priv::remove(size_t obj_idx) } -void Plater::priv::delete_object_from_model(size_t obj_idx) +bool Plater::priv::delete_object_from_model(size_t obj_idx) { + // check if object isn't cut + // show warning message that "cut consistancy" will not be supported any more + ModelObject* obj = model.objects[obj_idx]; + if (obj->is_cut()) { + MessageDialog dialog(q, _L("You try to delete an object which is a part of a cut object.\n" + "This action will break a cut correspondence.\n" + "After that PrusaSlicer can't garantie model consistency"), + _L("Delete object which is a part of cut object"), wxYES | wxCANCEL | wxCANCEL_DEFAULT); + dialog.SetButtonLabel(wxID_YES, _L("Delete object")); + if (dialog.ShowModal() == wxID_CANCEL) + return false; + // unmark all related CutParts of initial object + obj->invalidate_cut(); + } + wxString snapshot_label = _L("Delete Object"); - if (! model.objects[obj_idx]->name.empty()) - snapshot_label += ": " + wxString::FromUTF8(model.objects[obj_idx]->name.c_str()); + if (!obj->name.empty()) + snapshot_label += ": " + wxString::FromUTF8(obj->name.c_str()); Plater::TakeSnapshot snapshot(q, snapshot_label); m_worker.cancel_all(); model.delete_object(obj_idx); update(); object_list_changed(); + return true; } void Plater::priv::delete_all_objects_from_model() @@ -5679,7 +5695,7 @@ void Plater::reset_with_confirm() reset(); } -void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_model(obj_idx); } +bool Plater::delete_object_from_model(size_t obj_idx) { return p->delete_object_from_model(obj_idx); } void Plater::remove_selected() { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a168c32d1..39aa7cf8b 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -242,7 +242,7 @@ public: void remove(size_t obj_idx); void reset(); void reset_with_confirm(); - void delete_object_from_model(size_t obj_idx); + bool delete_object_from_model(size_t obj_idx); void remove_selected(); void increase_instances(size_t num = 1); void decrease_instances(size_t num = 1); From 87e1df2fb2bf15e3fe2862261c76c92b47498914 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 13 Apr 2022 13:16:29 +0200 Subject: [PATCH 25/93] Cut WIP: Lock icon is added for objects after a cut performing * ObjectDataViewModel: Some code refactoring to update bitmap in respect to the warning mane and lock appearance --- src/slic3r/GUI/GUI_ObjectList.cpp | 112 ++++++++++++++------ src/slic3r/GUI/GUI_ObjectList.hpp | 11 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 135 +++++++++++++++---------- src/slic3r/GUI/ObjectDataViewModel.hpp | 30 +++--- 4 files changed, 182 insertions(+), 106 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 3cb5bf3ce..9b2edb660 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1383,6 +1383,15 @@ bool ObjectList::is_instance_or_object_selected() return selection.is_single_full_instance() || selection.is_single_full_object(); } +bool ObjectList::is_selected_object_cut() +{ + const Selection& selection = scene_selection(); + int obj_idx = selection.get_object_idx(); + if (obj_idx < 0) + return false; + return object(obj_idx)->is_cut(); +} + void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false*/) { if (type == ModelVolumeType::INVALID && from_galery) { @@ -1779,22 +1788,22 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name #endif /* _DEBUG */ } -void ObjectList::del_object(const int obj_idx) +bool ObjectList::del_object(const int obj_idx) { - wxGetApp().plater()->delete_object_from_model(obj_idx); + return wxGetApp().plater()->delete_object_from_model(obj_idx); } // Delete subobject -void ObjectList::del_subobject_item(wxDataViewItem& item) +bool ObjectList::del_subobject_item(wxDataViewItem& item) { - if (!item) return; + if (!item) return false; int obj_idx, idx; ItemType type; m_objects_model->GetItemInfo(item, type, obj_idx, idx); if (type == itUndef) - return; + return false; wxDataViewItem parent = m_objects_model->GetParent(item); @@ -1808,10 +1817,8 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); else if (type & itInfo && obj_idx != -1) del_info_item(obj_idx, m_objects_model->GetInfoItemType(item)); - else if (idx == -1) - return; - else if (!del_subobject_from_object(obj_idx, idx, type)) - return; + else if (idx == -1 || !del_subobject_from_object(obj_idx, idx, type)) + return false; // If last volume item with warning was deleted, unmark object item if (type & itVolume) { @@ -1821,6 +1828,8 @@ void ObjectList::del_subobject_item(wxDataViewItem& item) m_objects_model->Delete(item); update_info_items(obj_idx); + + return true; } void ObjectList::del_info_item(const int obj_idx, InfoItemType type) @@ -1965,6 +1974,16 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object.")); return false; } + if (object->is_cut()) { + if (volume->is_model_part()) { + Slic3r::GUI::show_error(nullptr, _L("Solid part cannot be deleted from cut object.")); + return false; + } + if (volume->is_negative_volume()) { + Slic3r::GUI::show_error(nullptr, _L("Negative volume cannot be deleted from cut object.")); + return false; + } + } take_snapshot(_L("Delete Subobject")); @@ -1992,6 +2011,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con Slic3r::GUI::show_error(nullptr, _L("Last instance of an object cannot be deleted.")); return false; } + if (object->is_cut()) { + Slic3r::GUI::show_error(nullptr, _L("Instance cannot be deleted from cut object.")); + return false; + } take_snapshot(_L("Delete Instance")); object->delete_instance(idx); @@ -2735,7 +2758,8 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) const wxString& item_name = from_u8(model_object->name); const auto item = m_objects_model->Add(item_name, model_object->config.has("extruder") ? model_object->config.extruder() : 0, - get_warning_icon_name(model_object->mesh().stats())); + get_warning_icon_name(model_object->mesh().stats()), + model_object->is_cut()); update_info_items(obj_idx, nullptr, call_selection_changed); @@ -2805,29 +2829,40 @@ void ObjectList::delete_instance_from_list(const size_t obj_idx, const size_t in select_item([this, obj_idx, inst_idx]() { return m_objects_model->Delete(m_objects_model->GetItemByInstanceId(obj_idx, inst_idx)); }); } -void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx) +void ObjectList::update_lock_icons_for_model() { - if ( !(type&(itObject|itVolume|itInstance)) ) - return; - - take_snapshot(_(L("Delete Selected Item"))); - - if (type&itObject) { - del_object(obj_idx); - delete_object_from_list(obj_idx); - } - else { - del_subobject_from_object(obj_idx, sub_obj_idx, type); - - type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) : - delete_instance_from_list(obj_idx, sub_obj_idx); - } + for (int obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) + if (!(*m_objects)[obj_idx]->is_cut()) + m_objects_model->UpdateLockIcon(m_objects_model->GetItemById(obj_idx), false); } -void ObjectList::delete_from_model_and_list(const std::vector& items_for_delete) +bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx) +{ +// take_snapshot(_(L("Delete Selected Item"))); // #ysFIXME - delete this redundant snapshot after test + + if (type & (itObject | itVolume | itInstance)) { + if (type & itObject) { + bool was_cut = object(obj_idx)->is_cut(); + if (del_object(obj_idx)) { + delete_object_from_list(obj_idx); + if (was_cut) + update_lock_icons_for_model(); + return true; + } + } + else if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) { + type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) : + delete_instance_from_list(obj_idx, sub_obj_idx); + return true; + } + } + return false; +} + +bool ObjectList::delete_from_model_and_list(const std::vector& items_for_delete) { if (items_for_delete.empty()) - return; + return false; m_prevent_list_events = true; @@ -2836,8 +2871,12 @@ void ObjectList::delete_from_model_and_list(const std::vector& it if (!(item->type&(itObject | itVolume | itInstance))) continue; if (item->type&itObject) { - del_object(item->obj_idx); + bool was_cut = object(item->obj_idx)->is_cut(); + if (!del_object(item->obj_idx)) + continue; m_objects_model->Delete(m_objects_model->GetItemById(item->obj_idx)); + if (was_cut) + update_lock_icons_for_model(); } else { if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type)) @@ -2867,8 +2906,12 @@ void ObjectList::delete_from_model_and_list(const std::vector& it update_info_items(id); } - m_prevent_list_events = true; + m_prevent_list_events = false; + if (modified_objects_ids.empty()) + return false; part_selection_changed(); + + return true; } void ObjectList::delete_all_objects_from_list() @@ -2973,8 +3016,10 @@ void ObjectList::remove() { wxDataViewItem parent = m_objects_model->GetParent(item); ItemType type = m_objects_model->GetItemType(item); - if (type & itObject) - delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1); + if (type & itObject) { + if (!delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1)) + return item; + } else { if (type & (itLayer | itInstance)) { // In case there is just one layer or two instances and we delete it, del_subobject_item will @@ -2984,7 +3029,8 @@ void ObjectList::remove() parent = m_objects_model->GetTopParent(item); } - del_subobject_item(item); + if (!del_subobject_item(item)) + return item; } return parent; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 45072d4a5..25614581b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -247,7 +247,7 @@ public: void add_category_to_settings_from_frequent(const std::vector& category_options, wxDataViewItem item); void show_settings(const wxDataViewItem settings_item); bool is_instance_or_object_selected(); - + bool is_selected_object_cut(); void load_subobject(ModelVolumeType type, bool from_galery = false); // ! ysFIXME - delete commented code after testing and rename "load_modifier" to something common //void load_part(ModelObject& model_object, std::vector& added_volumes, ModelVolumeType type, bool from_galery = false); @@ -257,8 +257,8 @@ public: void load_shape_object_from_gallery(); void load_shape_object_from_gallery(const wxArrayString& input_files); void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true); - void del_object(const int obj_idx); - void del_subobject_item(wxDataViewItem& item); + bool del_object(const int obj_idx); + bool del_subobject_item(wxDataViewItem& item); void del_settings_from_config(const wxDataViewItem& parent_item); void del_instances_from_object(const int obj_idx); void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range); @@ -295,8 +295,9 @@ public: void delete_object_from_list(const size_t obj_idx); void delete_volume_from_list(const size_t obj_idx, const size_t vol_idx); void delete_instance_from_list(const size_t obj_idx, const size_t inst_idx); - void delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx); - void delete_from_model_and_list(const std::vector& items_for_delete); + void update_lock_icons_for_model(); + bool delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx); + bool delete_from_model_and_list(const std::vector& items_for_delete); // Delete all objects from the list void delete_all_objects_from_list(); // Increase instances count diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 908307125..619de379b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -38,6 +38,7 @@ static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; static constexpr char WarningManifoldIcon[] = "exclamation_manifold"; +static constexpr char LockIcon[] = "lock_closed_white"; struct InfoItemAtributes { std::string name; @@ -57,19 +58,15 @@ const std::map INFO_ITEMS{ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmap& bmp, const wxString& extruder, - const int idx/* = -1*/, - const std::string& warning_icon_name /*= std::string*/) : + const int idx/* = -1*/) : m_parent(parent), m_name(sub_obj_name), m_type(itVolume), m_volume_type(type), m_idx(idx), - m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : ""), - m_warning_icon_name(warning_icon_name) + m_extruder(type == Slic3r::ModelVolumeType::MODEL_PART || type == Slic3r::ModelVolumeType::PARAMETER_MODIFIER ? extruder : "") { - m_bmp = bmp; set_action_and_extruder_icons(); init_container(); } @@ -174,13 +171,6 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); } -void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name) -{ - m_warning_icon_name = warning_icon_name; - if (warning_icon_name.empty()) - m_bmp = m_empty_bmp; -} - void ObjectDataViewModelNode::update_settings_digest_bitmaps() { m_bmp = m_empty_bmp; @@ -328,6 +318,7 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon); + m_lock_bmp = create_scaled_bitmap(LockIcon); for (auto item : INFO_ITEMS) m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); @@ -341,19 +332,56 @@ ObjectDataViewModel::~ObjectDataViewModel() m_bitmap_cache = nullptr; } -wxBitmap& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name) +void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node) { - return warning_icon_name.empty() ? m_empty_bmp : warning_icon_name == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp; + int vol_type = static_cast(node->GetVolumeType()); + bool is_volume_node = vol_type >= 0; + + if (!node->has_warning_icon() && !node->has_lock()) { + node->SetBitmap(is_volume_node ? m_volume_bmps[vol_type] : m_empty_bmp); + return; + } + + std::string scaled_bitmap_name = std::string(); + if (node->has_warning_icon()) + scaled_bitmap_name += node->warning_icon_name(); + if (node->has_lock()) + scaled_bitmap_name += LockIcon; + if (is_volume_node) + scaled_bitmap_name += std::to_string(vol_type); + scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm"); + + wxBitmap* bmp = m_bitmap_cache->find(scaled_bitmap_name); + if (!bmp) { + std::vector bmps; + if (node->has_warning_icon()) + bmps.emplace_back(node->warning_icon_name() == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp); + if (node->has_lock()) + bmps.emplace_back(m_lock_bmp); + if (is_volume_node) + bmps.emplace_back(m_volume_bmps[vol_type]); + bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + } + + node->SetBitmap(*bmp); +} + +void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock) +{ + node->SetWarningIconName(warning_icon_name); + node->SetLock(has_lock); + UpdateBitmapForNode(node); } wxDataViewItem ObjectDataViewModel::Add(const wxString &name, const int extruder, - const std::string& warning_icon_name/* = std::string()*/ ) + const std::string& warning_icon_name, + const bool has_lock) { const wxString extruder_str = extruder == 0 ? _L("default") : wxString::Format("%d", extruder); auto root = new ObjectDataViewModelNode(name, extruder_str); // Add warning icon if detected auto-repaire - root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name); + UpdateBitmapForNode(root, warning_icon_name, has_lock); m_objects.push_back(root); // notify control @@ -384,7 +412,8 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent if (create_frst_child && root->m_volumes_cnt == 0) { const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART; - const auto node = new ObjectDataViewModelNode(root, root->m_name, type, GetVolumeIcon(type, root->m_warning_icon_name), extruder_str, 0, root->m_warning_icon_name); + const auto node = new ObjectDataViewModelNode(root, root->m_name, type, extruder_str, 0); + UpdateBitmapForNode(node, root->warning_icon_name(), root->has_lock()); insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); // notify control @@ -395,13 +424,16 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent if (insert_position >= 0) insert_position++; } - const auto node = new ObjectDataViewModelNode(root, name, volume_type, GetVolumeIcon(volume_type, warning_icon_name), extruder_str, root->m_volumes_cnt, warning_icon_name); + const auto node = new ObjectDataViewModelNode(root, name, volume_type, extruder_str, root->m_volumes_cnt); + UpdateBitmapForNode(node, warning_icon_name, root->has_lock() && volume_type < ModelVolumeType::PARAMETER_MODIFIER); insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); // if part with errors is added, but object wasn't marked, then mark it - if (!warning_icon_name.empty() && warning_icon_name != root->m_warning_icon_name && - (root->m_warning_icon_name.empty() || root->m_warning_icon_name == WarningManifoldIcon) ) - root->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name); + if (!warning_icon_name.empty() && warning_icon_name != root->warning_icon_name() && + (!root->has_warning_icon() || root->warning_icon_name() == WarningManifoldIcon)) { + root->SetWarningIconName(warning_icon_name); + UpdateBitmapForNode(root); + } // notify control const wxDataViewItem child((void*)node); @@ -1682,6 +1714,7 @@ void ObjectDataViewModel::Rescale() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = create_scaled_bitmap(WarningIcon); m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon); + m_lock_bmp = create_scaled_bitmap(LockIcon); for (auto item : INFO_ITEMS) m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); @@ -1700,10 +1733,8 @@ void ObjectDataViewModel::Rescale() switch (node->m_type) { case itObject: - if (node->m_bmp.IsOk()) node->m_bmp = GetWarningBitmap(node->m_warning_icon_name); - break; case itVolume: - node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name); + UpdateBitmapForNode(node); break; case itLayerRoot: node->m_bmp = create_scaled_bitmap(LayerRootIcon); @@ -1719,27 +1750,6 @@ void ObjectDataViewModel::Rescale() } } -wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/) -{ - if (warning_icon_name.empty()) - return m_volume_bmps[static_cast(vol_type)]; - - std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast(vol_type)); - scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm"); - - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); - if (bmp == nullptr) { - std::vector bmps; - - bmps.emplace_back(GetWarningBitmap(warning_icon_name)); - bmps.emplace_back(m_volume_bmps[static_cast(vol_type)]); - - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); - } - - return *bmp; -} - void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name) { if (!item.IsOk()) @@ -1747,13 +1757,14 @@ void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std:: ObjectDataViewModelNode *node = static_cast(item.GetID()); if (node->GetType() & itObject) { - node->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name); + UpdateBitmapForNode(node, warning_icon_name, node->has_lock()); return; } if (node->GetType() & itVolume) { - node->SetWarningBitmap(GetVolumeIcon(node->GetVolumeType(), warning_icon_name), warning_icon_name); - node->GetParent()->SetWarningBitmap(GetWarningBitmap(warning_icon_name), warning_icon_name); + UpdateBitmapForNode(node, warning_icon_name, node->has_lock()); + if (ObjectDataViewModelNode* parent = node->GetParent()) + UpdateBitmapForNode(parent, warning_icon_name, parent->has_lock()); return; } } @@ -1768,12 +1779,9 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject))) return; - if (node->GetType() & itVolume) { - node->SetWarningBitmap(m_volume_bmps[static_cast(node->volume_type())], ""); - return; - } + node->SetWarningIconName(std::string()); + UpdateBitmapForNode(node); - node->SetWarningBitmap(wxNullBitmap, ""); if (unmark_object) { wxDataViewItemArray children; @@ -1800,6 +1808,25 @@ void ObjectDataViewModel::UpdateWarningIcon(const wxDataViewItem& item, const st AddWarningIcon(item, warning_icon_name); } +void ObjectDataViewModel::UpdateLockIcon(const wxDataViewItem& item, bool has_lock) +{ + if (!item.IsOk()) + return; + ObjectDataViewModelNode* node = static_cast(item.GetID()); + if (node->has_lock() == has_lock) + return; + + node->SetLock(has_lock); + UpdateBitmapForNode(node); + + if (node->GetType() & itObject) { + wxDataViewItemArray children; + GetChildren(item, children); + for (const wxDataViewItem& child : children) + UpdateLockIcon(child, has_lock); + } +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 8669a8fd0..ec1d801c9 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -80,6 +80,7 @@ class ObjectDataViewModelNode PrintIndicator m_printable {piUndef}; wxBitmap m_printable_icon; std::string m_warning_icon_name{ "" }; + bool m_has_lock{false}; std::string m_action_icon_name = ""; ModelVolumeType m_volume_type; @@ -100,10 +101,8 @@ public: ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmap& bmp, const wxString& extruder, - const int idx = -1, - const std::string& warning_icon_name = std::string()); + const int idx = -1 ); ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const t_layer_height_range& layer_range, @@ -179,10 +178,11 @@ public: } bool SetValue(const wxVariant &variant, unsigned int col); - void SetVolumeType(ModelVolumeType type) { m_volume_type = type; } - void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } - void SetExtruder(const wxString &extruder) { m_extruder = extruder; } - void SetWarningBitmap(const wxBitmap& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; } + void SetVolumeType(ModelVolumeType type) { m_volume_type = type; } + void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } + void SetExtruder(const wxString &extruder) { m_extruder = extruder; } + void SetWarningIconName(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; } + void SetLock(bool has_lock) { m_has_lock = has_lock; } const wxBitmap& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } @@ -229,8 +229,6 @@ public: void set_extruder_icon(); // Set printable icon for node void set_printable_icon(PrintIndicator printable); - // Set warning icon for node - void set_warning_icon(const std::string& warning_icon); void update_settings_digest_bitmaps(); bool update_settings_digest(const std::vector& categories); @@ -241,7 +239,9 @@ public: bool valid(); #endif /* NDEBUG */ bool invalid() const { return m_idx < -1; } - bool has_warning_icon() const { return !m_warning_icon_name.empty(); } + bool has_warning_icon() const { return !m_warning_icon_name.empty(); } + bool has_lock() const { return m_has_lock; } + const std::string& warning_icon_name() const { return m_warning_icon_name; } private: friend class ObjectDataViewModel; @@ -263,6 +263,7 @@ class ObjectDataViewModel :public wxDataViewModel wxBitmap m_empty_bmp; wxBitmap m_warning_bmp; wxBitmap m_warning_manifold_bmp; + wxBitmap m_lock_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -272,7 +273,8 @@ public: wxDataViewItem Add( const wxString &name, const int extruder, - const std::string& warning_icon_name = std::string()); + const std::string& warning_icon_name, + const bool has_lock); wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item, const wxString &name, const Slic3r::ModelVolumeType volume_type, @@ -386,11 +388,10 @@ public: // Rescale bitmaps for existing Items void Rescale(); - wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, - const std::string& warning_icon_name = std::string()); void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); void UpdateWarningIcon(const wxDataViewItem& item, const std::string& warning_name); + void UpdateLockIcon(const wxDataViewItem& item, bool has_lock); bool HasWarningIcon(const wxDataViewItem& item) const; t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; @@ -404,7 +405,8 @@ private: wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); void AddAllChildren(const wxDataViewItem& parent); - wxBitmap& GetWarningBitmap(const std::string& warning_icon_name); + void UpdateBitmapForNode(ObjectDataViewModelNode* node); + void UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock); }; From 6fcb6afd81860d0ba19b9652687fa8a5d1c04df3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 14 Apr 2022 10:33:36 +0200 Subject: [PATCH 26/93] After merge fixes --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 26 ++++---------------------- src/slic3r/GUI/MeshUtils.cpp | 5 ----- src/slic3r/GUI/ObjectDataViewModel.cpp | 2 +- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 1db09956d..45f991c4c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -370,11 +370,7 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id) void GLGizmoCut3D::render_cut_plane() { #if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GL_SHADERS_ATTRIBUTES - GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); -#else GLShaderProgram* shader = wxGetApp().get_shader("flat"); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader == nullptr) return; @@ -467,11 +463,7 @@ void GLGizmoCut3D::render_cut_center_graber() glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glLineWidth(m_hover_id == m_group_id ? 2.0f : 1.5f)); -#if ENABLE_GL_SHADERS_ATTRIBUTES - GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); -#else GLShaderProgram* shader = wxGetApp().get_shader("flat"); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader != nullptr) { shader->start_using(); #if ENABLE_GL_SHADERS_ATTRIBUTES @@ -501,21 +493,15 @@ void GLGizmoCut3D::render_cut_center_graber() shader->stop_using(); } -#if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GL_SHADERS_ATTRIBUTES - shader = wxGetApp().get_shader("gouraud_light_attr"); -#else - shader = wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES -#else +#if !ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glColor3f(1.0, 1.0, 0.0)); ::glBegin(GL_LINES); ::glVertex3dv(plane_center.data()); ::glVertex3dv(m_grabbers[0].center.data()); glsafe(::glEnd()); +#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL + shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); @@ -745,7 +731,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) for (Axis axis : {X, Y, Z}) render_rotation_input(axis); - m_imgui->text(_L("°")); + m_imgui->text(_L("°")); } else { ImGui::AlignTextToFramePadding(); @@ -899,11 +885,7 @@ void GLGizmoCut3D::render_connectors(bool picking) return; #if ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GL_SHADERS_ATTRIBUTES - GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat_attr") : wxGetApp().get_shader("gouraud_light_attr"); -#else GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (shader == nullptr) return; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index cbea17d8e..58b8c7183 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -128,12 +128,7 @@ void MeshClipper::render_contour() if (curr_shader != nullptr) curr_shader->stop_using(); -#if ENABLE_GL_SHADERS_ATTRIBUTES - GLShaderProgram* shader = wxGetApp().get_shader("flat_attr"); -#else GLShaderProgram* shader = wxGetApp().get_shader("flat"); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - if (shader != nullptr) { shader->start_using(); #if ENABLE_GL_SHADERS_ATTRIBUTES diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 619de379b..9c5fac89e 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -38,7 +38,7 @@ static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; static constexpr char WarningManifoldIcon[] = "exclamation_manifold"; -static constexpr char LockIcon[] = "lock_closed_white"; +static constexpr char LockIcon[] = "lock_closed"; struct InfoItemAtributes { std::string name; From b5f565308ad9981a55228d92692a421fd47e8644 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 28 Apr 2022 13:14:57 +0200 Subject: [PATCH 27/93] Cut WIP: Added an extension for cut plane grabber + Information about build size during the normal of CutPlane is added to CutGizmo + Tooltip for cut plane grabber shows an info about heights of top and bottom parts now --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 182 ++++++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 + 2 files changed, 168 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 45f991c4c..4065c2060 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -24,6 +24,8 @@ namespace GUI { static const double Margin = 20.0; static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); +#define use_grabber_extension 1 + GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) , m_connector_type (CutConnectorType::Plug) @@ -59,12 +61,22 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, std::string GLGizmoCut3D::get_tooltip() const { std::string tooltip = m_rotation_gizmo.get_tooltip(); - if (tooltip.empty()) { - double koef = wxGetApp().app_config->get("use_inches") == "1" ? ObjectManipulation::mm_to_in : 1.0; - if (m_hover_id == m_group_id || m_grabbers[0].dragging) - return "X: " + format(m_plane_center.x() * koef, 2) + "; " +//"\n" + - "Y: " + format(m_plane_center.y() * koef, 2) + "; " +//"\n" + - "Z: " + format(m_plane_center.z() * koef, 2); + if (tooltip.empty() && + (m_hover_id == m_group_id || m_grabbers[0].dragging)) { + double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); + const BoundingBoxf3 tbb = transformed_bounding_box(); + if (tbb.max.z() >= 0.0) { + double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; + tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")"; + if (tbb.min.z() <= 0.0) + tooltip += "\n"; + } + if (tbb.min.z() <= 0.0) { + double bottom = (tbb.max.z() <= 0.0 ? tbb.size().z() : (tbb.min.z() * (-1))) * koef; + tooltip += format(bottom, 2) + " " + unit_str + " (" + _u8L("Bottom part") + ")"; + } + return tooltip; } return tooltip; @@ -449,15 +461,74 @@ void GLGizmoCut3D::render_cut_plane() void GLGizmoCut3D::render_cut_center_graber() { const Vec3d& angles = m_rotation_gizmo.get_rotation(); + m_grabbers[0].angles = angles; + m_grabbers[0].color = GRABBER_COLOR; + +#if use_grabber_extension + // UI experiments with grabber + + m_grabbers[0].center = m_plane_center; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (!shader) + return; + + auto color = m_hover_id == m_group_id ? complementary(GRABBER_COLOR) : GRABBER_COLOR; + m_sphere.set_color(color); + m_cone.set_color(color); + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Grabber& graber = m_grabbers.front(); + const Vec3d& center = graber.center; + + const BoundingBoxf3 box = bounding_box(); + + const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 6.0); + const double size = m_dragging ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + + const Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + const Vec3d offset = 1.25 * size * Vec3d::UnitZ(); + + shader->start_using(); + shader->set_uniform("emission_factor", 0.1f); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const Transform3d view_matrix = camera.get_view_matrix() * graber.matrix * + Geometry::assemble_transform(center, angles); + + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones()); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_sphere.render(); + + const BoundingBoxf3 tbb = transformed_bounding_box(); + if (tbb.max.z() >= 0.0) { + view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), scale); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_cone.render(); + } + + if (tbb.min.z() <= 0.0) { + view_model_matrix = view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), scale); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_cone.render(); + } + + shader->stop_using(); + +#else const BoundingBoxf3 box = bounding_box(); Vec3d grabber_center = m_plane_center; grabber_center[Z] += float(box.radius()/2.0); // Margin - rotate_vec3d_around_center(grabber_center, angles, m_plane_center); m_grabbers[0].center = grabber_center; - m_grabbers[0].angles = angles; glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); @@ -506,11 +577,11 @@ void GLGizmoCut3D::render_cut_center_graber() shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[0].color = GRABBER_COLOR; m_grabbers[0].render(m_hover_id == m_group_id, float((box.size().x() + box.size().y() + box.size().z()) / 3.0)); shader->stop_using(); } +#endif } bool GLGizmoCut3D::on_init() @@ -560,7 +631,13 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; if (m_hover_id == m_group_id) { +#if use_grabber_extension + Vec3d starting_box_center = m_plane_center; + starting_box_center[Z] -= 1.0; // some Margin + rotate_vec3d_around_center(starting_box_center, m_rotation_gizmo.get_rotation(), m_plane_center); +#else const Vec3d& starting_box_center = m_plane_center; +#endif const Vec3d& starting_drag_position = m_grabbers[0].center; double projection = 0.0; @@ -585,7 +662,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) Vec3d shift = starting_vec * projection; // move cut plane center - set_center(starting_box_center + shift); + set_center(m_plane_center + shift); } else if (m_hover_id > m_group_id && m_connector_mode == CutConnectorMode::Manual) @@ -624,6 +701,49 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const return ret; } +BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box() const +{ + // #ysFIXME !!! + BoundingBoxf3 ret; + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); + + const int instance_idx = selection.get_instance_idx(); + const int object_idx = selection.get_object_idx(); + if (instance_idx < 0 || object_idx < 0) + return ret; + + const Vec3d& instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); + + Vec3d cut_center_offset = m_plane_center - instance_offset; + cut_center_offset[Z] -= selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + + const Vec3d& rotation = m_rotation_gizmo.get_rotation(); + const auto move = Geometry::assemble_transform(-cut_center_offset, Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones() ); + const auto rot_z = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, 0, -rotation.z()), Vec3d::Ones(), Vec3d::Ones()); + const auto rot_y = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, -rotation.y(), 0), Vec3d::Ones(), Vec3d::Ones()); + const auto rot_x = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(-rotation.x(), 0, 0), Vec3d::Ones(), Vec3d::Ones()); + + const auto cut_matrix = rot_x * rot_y * rot_z * move; + + for (unsigned int i : idxs) { + const GLVolume* volume = selection.get_volume(i); + // respect just to the solid parts for FFF and ignore pad and supports for SLA + if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) { + + const auto instance_matrix = Geometry::assemble_transform( + Vec3d::Zero(), // don't apply offset + volume->get_instance_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), + volume->get_instance_scaling_factor(), + volume->get_instance_mirror() + ); + + ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * instance_matrix * volume->get_volume_transformation().get_matrix())); + } + } + return ret; +} + bool GLGizmoCut3D::update_bb() { const BoundingBoxf3 box = bounding_box(); @@ -634,6 +754,8 @@ bool GLGizmoCut3D::update_bb() set_center_pos(m_bb_center + m_center_offset); m_plane.reset(); + m_cone.reset(); + m_sphere.reset(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { m_selected.clear(); m_selected.resize(selection->model_object()->cut_connectors.size(), false); @@ -651,13 +773,18 @@ void GLGizmoCut3D::on_render() update_clipper_on_render(); } + if (!m_cone.is_initialized()) + m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); + if (!m_sphere.is_initialized()) + m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); + render_connectors(false); m_c->object_clipper()->render_cut(); if (!m_hide_cut_plane) { - render_cut_plane(); render_cut_center_graber(); + render_cut_plane(); if (m_mode == size_t(CutMode::cutPlanar)) { if (m_hover_id < m_group_id) m_rotation_gizmo.render(); @@ -732,16 +859,37 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) for (Axis axis : {X, Y, Z}) render_rotation_input(axis); m_imgui->text(_L("°")); + + ImGui::Separator(); + + double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); + + const BoundingBoxf3 tbb = transformed_bounding_box(); + Vec3d tbb_sz = tbb.size(); + wxString size = "X: " + double_to_string(tbb.size().x() * koef,2) + unit_str + + ", Y: " + double_to_string(tbb.size().y() * koef,2) + unit_str + + ", Z: " + double_to_string(tbb.size().z() * koef,2) + unit_str ; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build size")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); +#if 0 + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("DistToTop")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, double_to_string(tbb.max.z() * koef, 2)); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("DistToBottom")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, double_to_string(tbb.min.z() * koef, 2)); +#endif } else { ImGui::AlignTextToFramePadding(); - ImGui::AlignTextToFramePadding(); - ImGui::AlignTextToFramePadding(); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 3*m_control_width); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connect some two points of object to cteate a cut plane")); - ImGui::PopTextWrapPos(); - ImGui::AlignTextToFramePadding(); - ImGui::AlignTextToFramePadding(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ca49d2e4e..cb6d222ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -39,6 +39,8 @@ class GLGizmoCut3D : public GLGizmoBase #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; GLModel m_grabber_connection; + GLModel m_cone; + GLModel m_sphere; Vec3d m_old_center; #endif // ENABLE_LEGACY_OPENGL_REMOVAL @@ -117,6 +119,7 @@ public: void update_clipper_on_render(); BoundingBoxf3 bounding_box() const; + BoundingBoxf3 transformed_bounding_box() const; protected: bool on_init() override; From 94f3aaacd402cd12d9b54dcdf404866c3516ac5a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 2 May 2022 12:44:47 +0200 Subject: [PATCH 28/93] Cut WIP: Undo/Redo implementation --- resources/localization/list.txt | 5 ++ src/libslic3r/Model.hpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 91 +++++++++++++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 18 +++-- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 3 +- src/slic3r/GUI/Plater.cpp | 5 +- 6 files changed, 100 insertions(+), 25 deletions(-) diff --git a/resources/localization/list.txt b/resources/localization/list.txt index d68f99bff..f02b017d7 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -17,16 +17,21 @@ src/slic3r/GUI/GalleryDialog.cpp src/slic3r/GUI/GCodeViewer.cpp src/slic3r/GUI/GLCanvas3D.cpp src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +src/slic3r/GUI/Gizmos/GLGizmoCut.hpp src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp src/slic3r/GUI/Gizmos/GLGizmoMove.cpp src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp src/slic3r/GUI/Gizmos/GLGizmoScale.cpp src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp src/slic3r/GUI/Gizmos/GLGizmosManager.cpp src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp src/slic3r/GUI/GUI.cpp diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 108e30511..871859cf0 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -584,7 +584,8 @@ private: Internal::StaticSerializationWrapper layer_heigth_profile_wrapper(layer_height_profile); ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_heigth_profile_wrapper, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, - m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); + m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid, + cut_connectors, cut_id); } // Called by Print::validate() from the UI thread. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 4065c2060..6fafdbf9b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -14,6 +14,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/GUI_ObjectManipulation.hpp" +#include "slic3r/Utils/UndoRedo.hpp" #include "libslic3r/AppConfig.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/TriangleMeshSlicer.hpp" @@ -88,6 +89,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return false; if (m_rotation_gizmo.on_mouse(mouse_event)) { + if (mouse_event.LeftUp()) + on_stop_dragging(); update_clipper(); return true; } @@ -221,7 +224,7 @@ void GLGizmoCut3D::update_clipper() void GLGizmoCut3D::update_clipper_on_render() { update_clipper(); - suppress_update_clipper_on_render = true; + force_update_clipper_on_render = false; } void GLGizmoCut3D::set_center(const Vec3d& center) @@ -595,6 +598,27 @@ bool GLGizmoCut3D::on_init() return true; } +void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) +{ + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, //m_selected, + m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, + m_ar_plane_center, m_ar_rotations); + + set_center_pos(m_ar_plane_center); + m_rotation_gizmo.set_center(m_ar_plane_center); + m_rotation_gizmo.set_rotation(m_ar_rotations); + force_update_clipper_on_render = true; + + m_parent.request_extra_frame(); +} + +void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const +{ + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, //m_selected, + m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, + m_ar_plane_center, m_ar_rotations); +} + std::string GLGizmoCut3D::on_get_name() const { return _u8L("Cut"); @@ -602,13 +626,22 @@ std::string GLGizmoCut3D::on_get_name() const void GLGizmoCut3D::on_set_state() { - if (get_state() == On) + if (m_state == On) { update_bb(); + // initiate archived values + m_ar_plane_center = m_plane_center; + m_ar_rotations = m_rotation_gizmo.get_rotation(); + + m_parent.request_extra_frame(); + } + else + m_c->object_clipper()->release(); + m_rotation_gizmo.set_center(m_plane_center); m_rotation_gizmo.set_state(m_state); - suppress_update_clipper_on_render = m_state != On; + force_update_clipper_on_render = m_state == On; } void GLGizmoCut3D::on_set_hover_id() @@ -674,6 +707,24 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) } } +void GLGizmoCut3D::on_start_dragging() +{ + if (m_hover_id > m_group_id && m_connector_mode == CutConnectorMode::Manual) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move connector"), UndoRedo::SnapshotType::GizmoAction); +} + +void GLGizmoCut3D::on_stop_dragging() +{ + if (m_hover_id < m_group_id) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); + m_ar_rotations = m_rotation_gizmo.get_rotation(); + } + else if (m_hover_id == m_group_id) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); + m_ar_plane_center = m_plane_center; + } +} + void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) { m_plane_center = center_pos; @@ -778,6 +829,9 @@ void GLGizmoCut3D::on_render() if (!m_sphere.is_initialized()) m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); + if (force_update_clipper_on_render) + update_clipper_on_render(); + render_connectors(false); m_c->object_clipper()->render_cut(); @@ -790,9 +844,6 @@ void GLGizmoCut3D::on_render() m_rotation_gizmo.render(); } } - - if (!suppress_update_clipper_on_render) - update_clipper_on_render(); } void GLGizmoCut3D::on_render_for_picking() @@ -963,7 +1014,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); - m_imgui->disabled_begin((!m_keep_upper && !m_keep_lower) || !can_perform_cut()); + m_imgui->disabled_begin(!can_perform_cut()); const bool cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); @@ -1024,13 +1075,16 @@ void GLGizmoCut3D::render_connectors(bool picking) { m_has_invalid_connector = false; - if (m_connector_mode == CutConnectorMode::Auto) + if (m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) return; const ModelObject* mo = m_c->selection_info()->model_object(); const CutConnectors& connectors = mo->cut_connectors; - if (connectors.size() != m_selected.size()) - return; + if (connectors.size() != m_selected.size()) { + // #ysFIXME + m_selected.clear(); + m_selected.resize(connectors.size(), false); + } #if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); @@ -1145,7 +1199,7 @@ void GLGizmoCut3D::render_connectors(bool picking) bool GLGizmoCut3D::can_perform_cut() const { - if (m_has_invalid_connector) + if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower)) return false; BoundingBoxf3 box = bounding_box(); @@ -1172,9 +1226,16 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); + Plater* plater = wxGetApp().plater(); + bool create_dowels_as_separate_object = false; if (0.0 < object_cut_z && can_perform_cut()) { - ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; + ModelObject* mo = plater->model().objects[object_idx]; + if(!mo) + return; + + Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); + const bool has_connectors = !mo->cut_connectors.empty(); // update connectors pos as offset of its center before cut performing if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { @@ -1198,7 +1259,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) create_dowels_as_separate_object = true; } - wxGetApp().plater()->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), + plater->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | @@ -1306,7 +1367,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi const Vec3d& normal = pos_and_normal.second; // The clipping plane was clicked, hit containts coordinates of the hit in world coords. std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); connectors.emplace_back(hit, m_rotation_gizmo.get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); update_model_object(); @@ -1326,7 +1387,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (m_hover_id < m_connectors_group_id) return false; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector"), UndoRedo::SnapshotType::GizmoAction); size_t connector_id = m_hover_id - m_connectors_group_id; connectors.erase(connectors.begin() + connector_id); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index cb6d222ef..f8935d61f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -24,6 +24,10 @@ class GLGizmoCut3D : public GLGizmoBase double m_snap_step{ 1.0 }; int m_connectors_group_id; + // archived values + Vec3d m_ar_plane_center { Vec3d::Zero() }; + Vec3d m_ar_rotations { Vec3d::Zero() }; + Vec3d m_plane_center{ Vec3d::Zero() }; // data to check position of the cut palne center on gizmo activation Vec3d m_min_pos{ Vec3d::Zero() }; @@ -38,7 +42,7 @@ class GLGizmoCut3D : public GLGizmoBase #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; - GLModel m_grabber_connection; +// GLModel m_grabber_connection; GLModel m_cone; GLModel m_sphere; Vec3d m_old_center; @@ -57,7 +61,7 @@ class GLGizmoCut3D : public GLGizmoBase float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; bool m_imperial_units{ false }; - bool suppress_update_clipper_on_render{false}; + bool force_update_clipper_on_render{false}; mutable std::vector m_selected; // which pins are currently selected bool m_selection_empty = true; @@ -123,18 +127,24 @@ public: protected: bool on_init() override; - void on_load(cereal::BinaryInputArchive& ar) override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); } - void on_save(cereal::BinaryOutputArchive& ar) const override { ar(m_keep_upper, m_keep_lower, m_rotate_lower); } + void on_load(cereal::BinaryInputArchive& ar) override; + void on_save(cereal::BinaryOutputArchive& ar) const override; std::string on_get_name() const override; void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; void on_set_hover_id() override; bool on_is_activable() const override; void on_dragging(const UpdateData& data) override; + void on_start_dragging() override; + void on_stop_dragging() override; void on_render() override; void on_render_for_picking() override; void on_render_input_window(float x, float y, float bottom_limit) override; + bool wants_enter_leave_snapshots() const override { return true; } + std::string get_gizmo_entering_text() const override { return _u8L("Entering Cut gizmo"); } + std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Cut gizmo"); } + std::string get_action_snapshot_name() override { return _u8L("Cut gizmo editing"); } private: void set_center(const Vec3d& center); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index a5848013b..41f681894 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -836,7 +836,8 @@ void GLGizmoRotate3D::on_start_dragging() void GLGizmoRotate3D::on_stop_dragging() { assert(0 <= m_hover_id && m_hover_id < 3); - m_parent.do_rotate(L("Gizmo-Rotate")); + if (!m_use_only_grabbers) + m_parent.do_rotate(L("Gizmo-Rotate")); m_gizmos[m_hover_id].stop_dragging(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 00f2aee17..00274dfb5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5915,10 +5915,7 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) - return; - - Plater::TakeSnapshot snapshot(this, _L("Cut by Plane")); + //Plater::TakeSnapshot snapshot(this, _L("Cut by Plane")); wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes); From c903414005c20ceec4aba6684a8ad68525eebe1e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 3 May 2022 11:13:43 +0200 Subject: [PATCH 29/93] Cut WIP: Improved can_perform_cut() --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 13 +++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 6fafdbf9b..09e59d239 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -752,7 +752,7 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const return ret; } -BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box() const +BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false*/) const { // #ysFIXME !!! BoundingBoxf3 ret; @@ -774,8 +774,9 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box() const const auto rot_z = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, 0, -rotation.z()), Vec3d::Ones(), Vec3d::Ones()); const auto rot_y = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, -rotation.y(), 0), Vec3d::Ones(), Vec3d::Ones()); const auto rot_x = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(-rotation.x(), 0, 0), Vec3d::Ones(), Vec3d::Ones()); + const auto move2 = Geometry::assemble_transform(m_plane_center, Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones() ); - const auto cut_matrix = rot_x * rot_y * rot_z * move; + const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * rot_x * rot_y * rot_z * move; for (unsigned int i : idxs) { const GLVolume* volume = selection.get_volume(i); @@ -1202,12 +1203,8 @@ bool GLGizmoCut3D::can_perform_cut() const if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower)) return false; - BoundingBoxf3 box = bounding_box(); - double dist = (m_plane_center - box.center()).norm(); - if (dist > box.radius()) - return false; - - return true; + const BoundingBoxf3 tbb = transformed_bounding_box(true); + return tbb.contains(m_plane_center); } void GLGizmoCut3D::perform_cut(const Selection& selection) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f8935d61f..400018897 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -123,7 +123,7 @@ public: void update_clipper_on_render(); BoundingBoxf3 bounding_box() const; - BoundingBoxf3 transformed_bounding_box() const; + BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const; protected: bool on_init() override; From 90e359c5d4d718b9e34b0913ef550f54a0dc513b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 9 May 2022 11:23:05 +0200 Subject: [PATCH 30/93] Cut WIP: Implemented "Cut By Line" --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 226 +++++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 9 +- src/slic3r/GUI/MeshUtils.cpp | 18 +++ src/slic3r/GUI/MeshUtils.hpp | 3 + 4 files changed, 192 insertions(+), 64 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 09e59d239..7ad1d71f6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -39,7 +39,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, m_group_id = 3; m_connectors_group_id = 4; - m_modes = { _u8L("Planar"), _u8L("By Line"),_u8L("Grid") + m_modes = { _u8L("Planar"), _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") }; @@ -85,7 +85,7 @@ std::string GLGizmoCut3D::get_tooltip() const bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { - if (mouse_event.Moving()) + if (mouse_event.Moving() && !cut_line_processing()) return false; if (m_rotation_gizmo.on_mouse(mouse_event)) { @@ -147,6 +147,11 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return true; } } + else if (mouse_event.Moving()) { + // draw cut line + gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + return true; + } else if (pending_right_up && mouse_event.RightUp()) { pending_right_up = false; return true; @@ -212,6 +217,8 @@ void GLGizmoCut3D::update_clipper() // calculate normal and offset for clipping plane Vec3d normal = end - beg; + if (normal == Vec3d::Zero()) + return; dist = std::clamp(dist, 0.0001, normal.norm()); normal.normalize(); const double offset = normal.dot(beg) + dist; @@ -384,6 +391,9 @@ bool GLGizmoCut3D::render_revert_button(const std::string& label_id) void GLGizmoCut3D::render_cut_plane() { + if (cut_line_processing()) + return; + #if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) @@ -587,6 +597,45 @@ void GLGizmoCut3D::render_cut_center_graber() #endif } +void GLGizmoCut3D::render_cut_line() +{ + if (!cut_line_processing() || m_line_end == Vec3d::Zero()) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glLineWidth(2.0f)); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader != nullptr) { + shader->start_using(); +#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", camera.get_view_matrix()); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); +#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_cut_line.reset(); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::YELLOW(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex((Vec3f)m_line_beg.cast()); + init_data.add_vertex((Vec3f)m_line_end.cast()); + + // indices + init_data.add_line(0, 1); + + m_cut_line.init_from(std::move(init_data)); + m_cut_line.render(); + + shader->stop_using(); + } +} + bool GLGizmoCut3D::on_init() { m_grabbers.emplace_back(); @@ -770,11 +819,11 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* cut_center_offset[Z] -= selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); const Vec3d& rotation = m_rotation_gizmo.get_rotation(); - const auto move = Geometry::assemble_transform(-cut_center_offset, Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones() ); - const auto rot_z = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, 0, -rotation.z()), Vec3d::Ones(), Vec3d::Ones()); - const auto rot_y = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, -rotation.y(), 0), Vec3d::Ones(), Vec3d::Ones()); - const auto rot_x = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(-rotation.x(), 0, 0), Vec3d::Ones(), Vec3d::Ones()); - const auto move2 = Geometry::assemble_transform(m_plane_center, Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones() ); + const auto move = Geometry::assemble_transform(-cut_center_offset); + const auto rot_z = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, 0, -rotation.z())); + const auto rot_y = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, -rotation.y(), 0)); + const auto rot_x = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(-rotation.x(), 0, 0)); + const auto move2 = Geometry::assemble_transform(m_plane_center); const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * rot_x * rot_y * rot_z * move; @@ -840,11 +889,11 @@ void GLGizmoCut3D::on_render() if (!m_hide_cut_plane) { render_cut_center_graber(); render_cut_plane(); - if (m_mode == size_t(CutMode::cutPlanar)) { - if (m_hover_id < m_group_id) + if (m_hover_id < m_group_id && m_mode == size_t(CutMode::cutPlanar) && !cut_line_processing()) m_rotation_gizmo.render(); - } } + + render_cut_line(); } void GLGizmoCut3D::on_render_for_picking() @@ -886,63 +935,48 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - if (m_mode <= size_t(CutMode::cutByLine)) { + if (m_mode == size_t(CutMode::cutPlanar)) { + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); ImGui::Separator(); - if (m_mode == size_t(CutMode::cutPlanar)) { - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Move center")); + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Move center")); - m_imgui->disabled_begin(m_plane_center == bounding_box().center()); - revert_move = render_revert_button("move"); - m_imgui->disabled_end(); + m_imgui->disabled_begin(m_plane_center == bounding_box().center()); + revert_move = render_revert_button("move"); + m_imgui->disabled_end(); - for (Axis axis : {X, Y, Z}) - render_move_center_input(axis); - m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + for (Axis axis : {X, Y, Z}) + render_move_center_input(axis); + m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Rotation")); + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Rotation")); - m_imgui->disabled_begin(m_rotation_gizmo.get_rotation() == Vec3d::Zero()); - revert_rotation = render_revert_button("rotation"); - m_imgui->disabled_end(); + m_imgui->disabled_begin(m_rotation_gizmo.get_rotation() == Vec3d::Zero()); + revert_rotation = render_revert_button("rotation"); + m_imgui->disabled_end(); - for (Axis axis : {X, Y, Z}) - render_rotation_input(axis); - m_imgui->text(_L("°")); + for (Axis axis : {X, Y, Z}) + render_rotation_input(axis); + m_imgui->text(_L("°")); - ImGui::Separator(); + ImGui::Separator(); - double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); + double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); - const BoundingBoxf3 tbb = transformed_bounding_box(); - Vec3d tbb_sz = tbb.size(); - wxString size = "X: " + double_to_string(tbb.size().x() * koef,2) + unit_str + - ", Y: " + double_to_string(tbb.size().y() * koef,2) + unit_str + - ", Z: " + double_to_string(tbb.size().z() * koef,2) + unit_str ; + const BoundingBoxf3 tbb = transformed_bounding_box(); + Vec3d tbb_sz = tbb.size(); + wxString size = "X: " + double_to_string(tbb.size().x() * koef, 2) + unit_str + + ", Y: " + double_to_string(tbb.size().y() * koef, 2) + unit_str + + ", Z: " + double_to_string(tbb.size().z() * koef, 2) + unit_str; - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Build size")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); -#if 0 - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("DistToTop")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, double_to_string(tbb.max.z() * koef, 2)); - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("DistToBottom")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, double_to_string(tbb.min.z() * koef, 2)); -#endif - } - else { - ImGui::AlignTextToFramePadding(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connect some two points of object to cteate a cut plane")); - } + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build size")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); } m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); @@ -980,7 +1014,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->disabled_end(); - if (m_mode <= size_t(CutMode::cutByLine)) { + if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::Separator(); ImGui::AlignTextToFramePadding(); @@ -1074,12 +1108,15 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c void GLGizmoCut3D::render_connectors(bool picking) { - m_has_invalid_connector = false; - - if (m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) + if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) return; + m_has_invalid_connector = false; + const ModelObject* mo = m_c->selection_info()->model_object(); + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; const CutConnectors& connectors = mo->cut_connectors; if (connectors.size() != m_selected.size()) { // #ysFIXME @@ -1111,7 +1148,7 @@ void GLGizmoCut3D::render_connectors(bool picking) ColorRGBA render_color; - const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + const ModelInstance* mi = mo->instances[inst_id]; const Vec3d& instance_offset = mi->get_offset(); const float sla_shift = m_c->selection_info()->get_sla_shift(); @@ -1280,7 +1317,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; const Transform3d instance_trafo = sla_shift > 0.0 ? - Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift), Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones()) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); + Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); int mesh_id = -1; @@ -1340,6 +1377,67 @@ void GLGizmoCut3D::update_model_object() const m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } +bool GLGizmoCut3D::cut_line_processing() const +{ + return m_line_beg != Vec3d::Zero(); +} + +bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position) +{ + const float sla_shift = m_c->selection_info()->get_sla_shift(); + const ModelObject* mo = m_c->selection_info()->model_object(); + const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; + Transform3d inst_trafo = sla_shift > 0.0 ? + Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : + mi->get_transformation().get_matrix(); + + const Camera& camera = wxGetApp().plater()->get_camera(); + + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + ++mesh_id; + if (!mv->is_model_part()) + continue; + + const Transform3d trafo = inst_trafo * mv->get_matrix(); + const MeshRaycaster* raycaster = m_c->raycaster()->raycasters()[mesh_id]; + + Vec3d point; + Vec3d direction; + if (raycaster->unproject_on_mesh(mouse_position, trafo, camera, point, direction)) + { + point += mi->get_offset(); + point[Z] += sla_shift; + + if (action == SLAGizmoEventType::LeftDown && !cut_line_processing()) { + m_line_beg = point; + return true; + } + if (action == SLAGizmoEventType::Moving && cut_line_processing()) { + m_line_end = point; + return true; + } + if (action == SLAGizmoEventType::LeftDown && cut_line_processing()) { + Vec3f camera_dir = camera.get_dir_forward().cast(); + Vec3f line_dir = (m_line_end - m_line_beg).cast(); + + Vec3f cross_dir = line_dir.cross(camera_dir).normalized(); + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), cross_dir.cast()).toRotationMatrix(); + + m_rotation_gizmo.set_rotation(Geometry::Transformation(m).get_rotation()); + + set_center(Vec3d(0.5 * (point[X] + m_line_beg[X]), 0.5 * (point[Y] + m_line_beg[Y]), 0.5 * (point[Z] + m_line_beg[Z]))); + + m_line_end = m_line_beg = Vec3d::Zero(); + return true; + } + } + } + return false; +} + bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) @@ -1348,6 +1446,10 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; const Camera& camera = wxGetApp().plater()->get_camera(); + if ( m_hover_id < 0 && shift_down && + (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::Moving) ) + return process_cut_line(action, mouse_position); + int mesh_id = -1; // left down without selection rectangle - place connector on the cut plane: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 400018897..6edcded0f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -40,9 +40,12 @@ class GLGizmoCut3D : public GLGizmoBase // workaround for using of the clipping plane normal Vec3d m_clp_normal{ Vec3d::Ones() }; + Vec3d m_line_beg{ Vec3d::Zero() }; + Vec3d m_line_end{ Vec3d::Zero() }; + #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; -// GLModel m_grabber_connection; + GLModel m_cut_line; GLModel m_cone; GLModel m_sphere; Vec3d m_old_center; @@ -74,7 +77,6 @@ class GLGizmoCut3D : public GLGizmoBase enum class CutMode { cutPlanar - , cutByLine , cutGrig //,cutRadial //,cutModular @@ -159,15 +161,18 @@ private: void render_connectors(bool picking); bool can_perform_cut() const; + bool cut_line_processing() const; void render_cut_plane(); void render_cut_center_graber(); + void render_cut_line(); void perform_cut(const Selection& selection); void set_center_pos(const Vec3d& center_pos); bool update_bb(); void reset_connectors(); void update_connector_shape(); void update_model_object() const; + bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); }; } // namespace GUI diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 58b8c7183..7822a8182 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -406,6 +406,24 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& return true; } +bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& position, Vec3d& normal) const +{ + Vec3d point; + Vec3d direction; + line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); + + std::vector hits = m_emesh.query_ray_hits(point, direction); + + if (hits.empty()) + return false; // no intersection found + + // Now stuff the points in the provided vector and calculate normals if asked about them: + position = hits[0].position(); + normal = hits[0].normal(); + + return true; +} bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const { diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 2a25ddc73..621245394 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -153,6 +153,9 @@ public: bool* was_clipping_plane_hit = nullptr // is the hit on the clipping place cross section? ) const; + // Given a mouse position, this returns true in case it is on the mesh. + bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& position, Vec3d& normal) const; + bool is_valid_intersection(Vec3d point, Vec3d direction, const Transform3d& trafo) const; // Given a vector of points in woorld coordinates, this returns vector From 51e77fd81b440d203df2b1341f81d42fa877732f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 9 May 2022 16:13:13 +0200 Subject: [PATCH 31/93] Cut WIP: Make negative volumes a little bit "dipper" + Some improvements for Undo/Redo stack --- src/libslic3r/Model.cpp | 10 ++++-- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 50 ++++++++++++++++------------ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index a034787ac..8a8bb9490 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1511,11 +1511,17 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (!volume->is_model_part()) { if (volume->source.is_connector) { - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { ModelVolume* vol = upper->add_volume(*volume); + // make a "hole" dipper + vol->set_scaling_factor(Z, 1.1 * vol->get_scaling_factor(Z)); + } if (attributes.has(ModelObjectCutAttribute::KeepLower)) { ModelVolume* vol = lower->add_volume(*volume); - if (!attributes.has(ModelObjectCutAttribute::CreateDowels)) + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) + // make a "hole" dipper + vol->set_scaling_factor(Z, 1.2 * vol->get_scaling_factor(Z)); + else // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7ad1d71f6..da2b28dcd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -714,8 +714,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) if (m_hover_id == m_group_id) { #if use_grabber_extension - Vec3d starting_box_center = m_plane_center; - starting_box_center[Z] -= 1.0; // some Margin + Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ();// some Margin rotate_vec3d_around_center(starting_box_center, m_rotation_gizmo.get_rotation(), m_plane_center); #else const Vec3d& starting_box_center = m_plane_center; @@ -1155,7 +1154,7 @@ void GLGizmoCut3D::render_connectors(bool picking) const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; - const Transform3d instance_trafo = Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift), Vec3d::Zero(), Vec3d::Ones(), Vec3d::Ones()) * mi->get_transformation().get_matrix(); + const Transform3d instance_trafo = Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix(); for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; @@ -1268,29 +1267,34 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) if(!mo) return; - Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); - const bool has_connectors = !mo->cut_connectors.empty(); - // update connectors pos as offset of its center before cut performing - if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { - for (CutConnector& connector : mo->cut_connectors) { - connector.rotation = m_rotation_gizmo.get_rotation(); + { + Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); + // update connectors pos as offset of its center before cut performing + if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { + for (CutConnector& connector : mo->cut_connectors) { + connector.rotation = m_rotation_gizmo.get_rotation(); - if (m_connector_type == CutConnectorType::Dowel) { - if (m_connector_style == size_t(CutConnectorStyle::Prizm)) - connector.height *= 2; - } - else { - // culculate shift of the connector center regarding to the position on the cut plane - Vec3d norm = m_grabbers[0].center - m_plane_center; - norm.normalize(); - Vec3d shift = norm * (0.5 * connector.height); - connector.pos += shift; + if (m_connector_type == CutConnectorType::Dowel) { + if (m_connector_style == size_t(CutConnectorStyle::Prizm)) + connector.height *= 2; + } + else { + // culculate shift of the connector center regarding to the position on the cut plane +#if use_grabber_extension + Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); + rotate_vec3d_around_center(shifted_center, m_rotation_gizmo.get_rotation(), m_plane_center); + Vec3d norm = (shifted_center - m_plane_center).normalized(); +#else + Vec3d norm = (m_grabbers[0].center - m_plane_center).normalize(); +#endif + connector.pos += norm * (0.5 * connector.height); + } } + mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); + if (m_connector_type == CutConnectorType::Dowel) + create_dowels_as_separate_object = true; } - mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); - if (m_connector_type == CutConnectorType::Dowel) - create_dowels_as_separate_object = true; } plater->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), @@ -1421,6 +1425,8 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse Vec3f camera_dir = camera.get_dir_forward().cast(); Vec3f line_dir = (m_line_end - m_line_beg).cast(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); + Vec3f cross_dir = line_dir.cross(camera_dir).normalized(); Eigen::Quaterniond q; Transform3d m = Transform3d::Identity(); From 279b116533843aa406850427fad7d831c4ea53ec Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 9 May 2022 16:52:02 +0200 Subject: [PATCH 32/93] Cut WIP: Fix compilation warnings and errors --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 19 ++++++------------- src/slic3r/GUI/Plater.cpp | 9 +++++---- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index da2b28dcd..b83d4ffbb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -255,8 +255,6 @@ bool GLGizmoCut3D::render_combo(const std::string& label, const std::vectortext(_L("Build size")); @@ -1158,7 +1155,7 @@ void GLGizmoCut3D::render_connectors(bool picking) for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; - const bool& point_selected = m_selected[i]; +// const bool& point_selected = m_selected[i]; double height = connector.height; // recalculate connector position to world position @@ -1449,14 +1446,11 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) return false; - CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - const Camera& camera = wxGetApp().plater()->get_camera(); - if ( m_hover_id < 0 && shift_down && (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::Moving) ) return process_cut_line(action, mouse_position); - int mesh_id = -1; + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; // left down without selection rectangle - place connector on the cut plane: if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down) { @@ -1469,7 +1463,6 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi std::pair pos_and_normal; if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { const Vec3d& hit = pos_and_normal.first; - const Vec3d& normal = pos_and_normal.second; // The clipping plane was clicked, hit containts coordinates of the hit in world coords. std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a1773c649..a1694dc17 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2977,10 +2977,11 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) // show warning message that "cut consistancy" will not be supported any more ModelObject* obj = model.objects[obj_idx]; if (obj->is_cut()) { - MessageDialog dialog(q, _L("You try to delete an object which is a part of a cut object.\n" - "This action will break a cut correspondence.\n" - "After that PrusaSlicer can't garantie model consistency"), - _L("Delete object which is a part of cut object"), wxYES | wxCANCEL | wxCANCEL_DEFAULT); + InfoDialog dialog(q, _L("Delete object which is a part of cut object"), + _L("You try to delete an object which is a part of a cut object.\n" + "This action will break a cut correspondence.\n" + "After that PrusaSlicer can't garantie model consistency"), + false, wxYES | wxCANCEL | wxCANCEL_DEFAULT | wxICON_WARNING); dialog.SetButtonLabel(wxID_YES, _L("Delete object")); if (dialog.ShowModal() == wxID_CANCEL) return false; From d6f46bedeb73bb717cd6e8138a75a872481f0fcd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 May 2022 13:23:59 +0200 Subject: [PATCH 33/93] Cut WIP: Fixed some bugs * Fixed a crash on adding of the object (ObjectDataViewModel:volume_type wasn't initialized) * Cur grabber color is changed to YELLOW * Check position of the cut plane center on moving of the cut plane --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 10 +++++++--- src/slic3r/GUI/ObjectDataViewModel.hpp | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index b83d4ffbb..5e8c07dd9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -23,7 +23,7 @@ namespace Slic3r { namespace GUI { static const double Margin = 20.0; -static const ColorRGBA GRABBER_COLOR = ColorRGBA::ORANGE(); +static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); #define use_grabber_extension 1 @@ -163,7 +163,7 @@ void GLGizmoCut3D::shift_cut_z(double delta) { Vec3d new_cut_center = m_plane_center; new_cut_center[Z] += delta; - set_center_pos(new_cut_center); + set_center(new_cut_center); } void GLGizmoCut3D::rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center) @@ -616,7 +616,7 @@ void GLGizmoCut3D::render_cut_line() GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA::YELLOW(); + init_data.color = GRABBER_COLOR; init_data.reserve_vertices(2); init_data.reserve_indices(2); @@ -773,6 +773,10 @@ void GLGizmoCut3D::on_stop_dragging() void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) { + const BoundingBoxf3 tbb = transformed_bounding_box(true); + if (!tbb.contains(center_pos)) + return; + m_plane_center = center_pos; // !!! ysFIXME add smart clamp calculation diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index ec1d801c9..f5c61a6d4 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -83,7 +83,7 @@ class ObjectDataViewModelNode bool m_has_lock{false}; std::string m_action_icon_name = ""; - ModelVolumeType m_volume_type; + ModelVolumeType m_volume_type{ -1 }; InfoItemType m_info_item_type {InfoItemType::Undef}; public: From 496481e9726e78a0bd50f3822aa98aeac43888fd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 12 May 2022 17:07:13 +0200 Subject: [PATCH 34/93] Cut WIP: Fix for Undo/Redo --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 43 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 5 ++- src/slic3r/GUI/Plater.cpp | 38 ++++++++++++++------- 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5e8c07dd9..820124a7e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -651,7 +651,7 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, m_ar_plane_center, m_ar_rotations); - set_center_pos(m_ar_plane_center); + set_center_pos(m_ar_plane_center, true); m_rotation_gizmo.set_center(m_ar_plane_center); m_rotation_gizmo.set_rotation(m_ar_rotations); force_update_clipper_on_render = true; @@ -771,21 +771,12 @@ void GLGizmoCut3D::on_stop_dragging() } } -void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos) +void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/) { - const BoundingBoxf3 tbb = transformed_bounding_box(true); - if (!tbb.contains(center_pos)) - return; - - m_plane_center = center_pos; - - // !!! ysFIXME add smart clamp calculation - // Clamp the center position of the cut plane to the object's bounding box - //m_plane_center = Vec3d(std::clamp(center_pos.x(), m_min_pos.x(), m_max_pos.x()), - // std::clamp(center_pos.y(), m_min_pos.y(), m_max_pos.y()), - // std::clamp(center_pos.z(), m_min_pos.z(), m_max_pos.z())); - - m_center_offset = m_plane_center - m_bb_center; + if (force || transformed_bounding_box(true).contains(center_pos)) { + m_plane_center = center_pos; + m_center_offset = m_plane_center - m_bb_center; + } } BoundingBoxf3 GLGizmoCut3D::bounding_box() const @@ -806,18 +797,18 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* { // #ysFIXME !!! BoundingBoxf3 ret; - const Selection& selection = m_parent.get_selection(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - const int instance_idx = selection.get_instance_idx(); - const int object_idx = selection.get_object_idx(); - if (instance_idx < 0 || object_idx < 0) + const ModelObject* mo = m_c->selection_info()->model_object(); + if (!mo) return ret; + const int instance_idx = m_c->selection_info()->get_active_instance(); + if (instance_idx < 0) + return ret; + const ModelInstance* mi = mo->instances[instance_idx]; - const Vec3d& instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); - + const Vec3d& instance_offset = mi->get_offset(); Vec3d cut_center_offset = m_plane_center - instance_offset; - cut_center_offset[Z] -= selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + cut_center_offset[Z] -= m_c->selection_info()->get_sla_shift(); const Vec3d& rotation = m_rotation_gizmo.get_rotation(); const auto move = Geometry::assemble_transform(-cut_center_offset); @@ -828,6 +819,8 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * rot_x * rot_y * rot_z * move; + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList& idxs = selection.get_volume_idxs(); for (unsigned int i : idxs) { const GLVolume* volume = selection.get_volume(i); // respect just to the solid parts for FFF and ignore pad and supports for SLA @@ -853,7 +846,7 @@ bool GLGizmoCut3D::update_bb() m_max_pos = box.max; m_min_pos = box.min; m_bb_center = box.center(); - set_center_pos(m_bb_center + m_center_offset); + set_center_pos(m_bb_center + m_center_offset, true); m_plane.reset(); m_cone.reset(); @@ -1270,9 +1263,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) const bool has_connectors = !mo->cut_connectors.empty(); { - Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); // update connectors pos as offset of its center before cut performing if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { + Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); for (CutConnector& connector : mo->cut_connectors) { connector.rotation = m_rotation_gizmo.get_rotation(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 6edcded0f..439abea8d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -167,7 +167,7 @@ private: void render_cut_center_graber(); void render_cut_line(); void perform_cut(const Selection& selection); - void set_center_pos(const Vec3d& center_pos); + void set_center_pos(const Vec3d& center_pos, bool force = false); bool update_bb(); void reset_connectors(); void update_connector_shape(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 7f60892b1..e7ba37010 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -414,8 +414,11 @@ void ObjectClipper::render_cut() const if (m_clp_ratio == 0.) return; const SelectionInfo* sel_info = get_pool()->selection_info(); + int sel_instance_idx = sel_info->get_active_instance(); + if (sel_instance_idx < 0) + return; const ModelObject* mo = sel_info->model_object(); - const Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation(); + const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); size_t clipper_id = 0; for (const ModelVolume* mv : mo->volumes) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a1694dc17..26c7da784 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1780,7 +1780,7 @@ struct Plater::priv std::string get_config(const std::string &key) const; std::vector load_files(const std::vector& input_files, bool load_model, bool load_config, bool used_inches = false); - std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false); + std::vector load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool call_selection_changed = true); fs::path get_export_file_path(GUI::FileType file_type); wxString get_export_file(GUI::FileType file_type); @@ -2691,7 +2691,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ // #define AUTOPLACEMENT_ON_LOAD -std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z) +std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool call_selection_changed /*= true*/) { const Vec3d bed_size = Slic3r::to_3d(this->bed.build_volume().bounding_volume2d().size(), 1.0) - 2.0 * Vec3d::Ones(); @@ -2774,17 +2774,18 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs& mode notification_manager->close_notification_of_type(NotificationType::UpdatedItemsInfo); for (const size_t idx : obj_idxs) { - wxGetApp().obj_list()->add_object_to_list(idx); + wxGetApp().obj_list()->add_object_to_list(idx, call_selection_changed); } - update(); - // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), - // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call - for (const size_t idx : obj_idxs) - wxGetApp().obj_list()->update_info_items(idx); - - object_list_changed(); + if (call_selection_changed) { + update(); + // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), + // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call + for (const size_t idx : obj_idxs) + wxGetApp().obj_list()->update_info_items(idx); + object_list_changed(); + } this->schedule_background_process(); return obj_idxs; @@ -5919,18 +5920,29 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - //Plater::TakeSnapshot snapshot(this, _L("Cut by Plane")); + this->suppress_snapshots(); wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes); - remove(obj_idx); - p->load_model_objects(new_objects); + model().delete_object(obj_idx); + sidebar().obj_list()->delete_object_from_list(obj_idx); + + // suppress to call selection update for Object List to avoid call of early Gizmos on/off update + p->load_model_objects(new_objects, false, false); Selection& selection = p->get_selection(); size_t last_id = p->model.objects.size() - 1; for (size_t i = 0; i < new_objects.size(); ++i) selection.add_object((unsigned int)(last_id - i), i == 0); + this->allow_snapshots(); + + // now process all updates of the 3d scene + update(); + // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), + // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call + for (size_t idx = 0; idx < p->model.objects.size(); idx++) + wxGetApp().obj_list()->update_info_items(idx); } void Plater::export_gcode(bool prefer_removable) From ef26b1abebd56d8a1f6014b49e633b66d8d4fad7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 16 May 2022 11:44:30 +0200 Subject: [PATCH 35/93] Cut gizmo: cut by line does not rely on mesh raycasters --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 62 +++++++++++----------------- src/slic3r/GUI/MeshUtils.cpp | 2 +- src/slic3r/GUI/MeshUtils.hpp | 4 +- 3 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 820124a7e..6a8a99856 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1391,49 +1391,37 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse const Camera& camera = wxGetApp().plater()->get_camera(); - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - ++mesh_id; - if (!mv->is_model_part()) - continue; + Vec3d pt; + Vec3d dir; + MeshRaycaster::line_from_mouse_pos(mouse_position, Transform3d::Identity(), camera, pt, dir); + dir.normalize(); + pt += dir; // Move the pt along dir so it is not clipped. - const Transform3d trafo = inst_trafo * mv->get_matrix(); - const MeshRaycaster* raycaster = m_c->raycaster()->raycasters()[mesh_id]; + if (action == SLAGizmoEventType::LeftDown && !cut_line_processing()) { + m_line_beg = pt; + m_line_end = pt; + return true; + } - Vec3d point; - Vec3d direction; - if (raycaster->unproject_on_mesh(mouse_position, trafo, camera, point, direction)) - { - point += mi->get_offset(); - point[Z] += sla_shift; + if (cut_line_processing()) { + m_line_end = pt; + if (action == SLAGizmoEventType::LeftDown) { + Vec3d point = m_line_end; + Vec3d line_dir = m_line_end - m_line_beg; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); - if (action == SLAGizmoEventType::LeftDown && !cut_line_processing()) { - m_line_beg = point; - return true; - } - if (action == SLAGizmoEventType::Moving && cut_line_processing()) { - m_line_end = point; - return true; - } - if (action == SLAGizmoEventType::LeftDown && cut_line_processing()) { - Vec3f camera_dir = camera.get_dir_forward().cast(); - Vec3f line_dir = (m_line_end - m_line_beg).cast(); + Vec3d cross_dir = line_dir.cross(dir).normalized(); + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), cross_dir).toRotationMatrix(); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); + m_rotation_gizmo.set_rotation(Geometry::Transformation(m).get_rotation()); - Vec3f cross_dir = line_dir.cross(camera_dir).normalized(); - Eigen::Quaterniond q; - Transform3d m = Transform3d::Identity(); - m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), cross_dir.cast()).toRotationMatrix(); + set_center(m_plane_center + cross_dir * (cross_dir.dot(pt - m_plane_center))); - m_rotation_gizmo.set_rotation(Geometry::Transformation(m).get_rotation()); - - set_center(Vec3d(0.5 * (point[X] + m_line_beg[X]), 0.5 * (point[Y] + m_line_beg[Y]), 0.5 * (point[Z] + m_line_beg[Z]))); - - m_line_end = m_line_beg = Vec3d::Zero(); - return true; - } + m_line_end = m_line_beg = Vec3d::Zero(); } + return true; } return false; } @@ -1461,7 +1449,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { const Vec3d& hit = pos_and_normal.first; // The clipping plane was clicked, hit containts coordinates of the hit in world coords. - std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; + //std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); connectors.emplace_back(hit, m_rotation_gizmo.get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 7822a8182..171979406 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -324,7 +324,7 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const } void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const + Vec3d& point, Vec3d& direction) { Matrix4d modelview = camera.get_view_matrix().matrix(); Matrix4d projection= camera.get_projection_matrix().matrix(); diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 621245394..9c8bc8e67 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -138,8 +138,8 @@ public: { } - void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, - Vec3d& point, Vec3d& direction) const; + static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, + Vec3d& point, Vec3d& direction); // Given a mouse position, this returns true in case it is on the mesh. bool unproject_on_mesh( From 7129ee382971e8fa5c9c6dc3443e558bdc332ed2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 May 2022 15:26:43 +0200 Subject: [PATCH 36/93] Merge branch 'tm_curl_new' (cherry-picked from master) --- CMakeLists.txt | 33 ++++++++++++++++----------------- deps/CMakeLists.txt | 9 +++++++-- deps/CURL/CURL.cmake | 14 ++++++++------ deps/deps-macos.cmake | 5 +++++ src/CMakeLists.txt | 2 +- src/libslic3r/CMakeLists.txt | 2 +- src/slic3r/CMakeLists.txt | 4 ---- 7 files changed, 38 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a486000f..002cd3456 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,14 @@ set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux") set(IS_CROSS_COMPILE FALSE) +if (SLIC3R_STATIC) + # Prefer config scripts over find modules. This is helpful when building with + # the static dependencies. Many libraries have their own export scripts + # while having a Find module in standard cmake installation. + # (e.g. CURL) + set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +endif () + if (APPLE) set(CMAKE_FIND_FRAMEWORK LAST) set(CMAKE_FIND_APPBUNDLE LAST) @@ -438,23 +446,6 @@ else() target_link_libraries(libcurl INTERFACE crypt32) endif() -if (SLIC3R_STATIC AND NOT SLIC3R_STATIC_EXCLUDE_CURL) - if (NOT APPLE) - # libcurl is always linked dynamically to the system libcurl on OSX. - # On other systems, libcurl is linked statically if SLIC3R_STATIC is set. - target_compile_definitions(libcurl INTERFACE CURL_STATICLIB) - endif() - if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - # As of now, our build system produces a statically linked libcurl, - # which links the OpenSSL library dynamically. - find_package(OpenSSL REQUIRED) - message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}") - message("OpenSSL libraries: ${OPENSSL_LIBRARIES}") - target_include_directories(libcurl INTERFACE ${OPENSSL_INCLUDE_DIR}) - target_link_libraries(libcurl INTERFACE ${OPENSSL_LIBRARIES}) - endif() -endif() - ## OPTIONAL packages # Find eigen3 or use bundled version @@ -472,6 +463,14 @@ include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR}) find_package(EXPAT REQUIRED) +add_library(libexpat INTERFACE) + +if (TARGET EXPAT::EXPAT ) + target_link_libraries(libexpat INTERFACE EXPAT::EXPAT) +elseif(TARGET expat::expat) + target_link_libraries(libexpat INTERFACE expat::expat) +endif () + find_package(PNG REQUIRED) set(OpenGL_GL_PREFERENCE "LEGACY") diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 94daee85f..d129ff1c2 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -179,7 +179,12 @@ include(CGAL/CGAL.cmake) include(NLopt/NLopt.cmake) include(OpenSSL/OpenSSL.cmake) -include(CURL/CURL.cmake) + +set(CURL_PKG "") +if (NOT CURL_FOUND) + include(CURL/CURL.cmake) + set(CURL_PKG dep_CURL) +endif () include(JPEG/JPEG.cmake) include(TIFF/TIFF.cmake) @@ -188,7 +193,7 @@ include(wxWidgets/wxWidgets.cmake) set(_dep_list dep_Boost dep_TBB - dep_CURL + ${CURL_PKG} dep_wxWidgets dep_Cereal dep_NLopt diff --git a/deps/CURL/CURL.cmake b/deps/CURL/CURL.cmake index a05a4e97e..579a27f66 100644 --- a/deps/CURL/CURL.cmake +++ b/deps/CURL/CURL.cmake @@ -48,11 +48,13 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") ) endif () -if (BUILD_SHARED_LIBS) - set(_curl_static OFF) -else() - set(_curl_static ON) -endif() +set(_patch_command "") +if (UNIX AND NOT APPLE) + # On non-apple UNIX platforms, finding the location of OpenSSL certificates is necessary at runtime, as there is no standard location usable across platforms. + # The OPENSSL_CERT_OVERRIDE flag is understood by PrusaSlicer and will trigger the search of certificates at initial application launch. + # Then ask the user for consent about the correctness of the found location. + set (_patch_command echo set_target_properties(CURL::libcurl PROPERTIES INTERFACE_COMPILE_DEFINITIONS OPENSSL_CERT_OVERRIDE) >> CMake/curl-config.cmake.in) +endif () prusaslicer_add_cmake_project(CURL # GIT_REPOSITORY https://github.com/curl/curl.git @@ -62,10 +64,10 @@ prusaslicer_add_cmake_project(CURL DEPENDS ${ZLIB_PKG} # PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df && # ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/curl-mods.patch + PATCH_COMMAND "${_patch_command}" CMAKE_ARGS -DBUILD_TESTING:BOOL=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON - -DCURL_STATICLIB=${_curl_static} ${_curl_platform_flags} ) diff --git a/deps/deps-macos.cmake b/deps/deps-macos.cmake index 42afc623d..d9e0ce377 100644 --- a/deps/deps-macos.cmake +++ b/deps/deps-macos.cmake @@ -15,6 +15,11 @@ set(DEP_CMAKE_OPTS include("deps-unix-common.cmake") +find_package(CURL QUIET) +if (NOT CURL_FOUND) + message(WARNING "No CURL dev package found in system, building static library. Mac SDK should include CURL from at least version 10.12. Check your SDK installation.") +endif () + # ExternalProject_Add(dep_boost # EXCLUDE_FROM_ALL 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8430e968..801760b8c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,7 +92,7 @@ if (SLIC3R_GUI) string(REGEX MATCH "wxexpat" WX_EXPAT_BUILTIN ${wxWidgets_LIBRARIES}) if (EXPAT_FOUND AND NOT WX_EXPAT_BUILTIN) list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat) - list(APPEND wxWidgets_LIBRARIES EXPAT::EXPAT) + list(APPEND wxWidgets_LIBRARIES libexpat) endif () # This is an issue in the new wxWidgets cmake build, doesn't deal with librt diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c9d8aa4fa..396eb0764 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -372,7 +372,7 @@ target_link_libraries(libslic3r boost_libs clipper nowide - EXPAT::EXPAT + libexpat glu-libtess qhull semver diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index ef7687f00..ed994be18 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -288,10 +288,6 @@ if (SLIC3R_STATIC) target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0) endif() -if (SLIC3R_STATIC AND NOT SLIC3R_STATIC_EXCLUDE_CURL AND UNIX AND NOT APPLE) - target_compile_definitions(libslic3r_gui PRIVATE OPENSSL_CERT_OVERRIDE) -endif () - if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () From c695dcc141e5bb1d1a2cd5bb6f56bfb301b978d7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 16 May 2022 16:24:35 +0200 Subject: [PATCH 37/93] Cut gizmo: UI simplification and changes --- src/imgui/imconfig.h | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 276 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 1 + 6 files changed, 166 insertions(+), 119 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index dcb2d2338..9a29789d3 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -169,6 +169,7 @@ namespace ImGui const wchar_t LegendCOG = 0x2615; const wchar_t LegendShells = 0x2616; const wchar_t LegendToolMarker = 0x2617; + const wchar_t WarningMarkerSmall = 0x2618; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 6a8a99856..58eb19a04 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -19,6 +19,8 @@ #include "libslic3r/Model.hpp" #include "libslic3r/TriangleMeshSlicer.hpp" +#include "imgui/imgui_internal.h" + namespace Slic3r { namespace GUI { @@ -39,7 +41,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, m_group_id = 3; m_connectors_group_id = 4; - m_modes = { _u8L("Planar"), _u8L("Grid") + m_modes = { _u8L("Planar")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") }; @@ -471,6 +473,8 @@ void GLGizmoCut3D::render_cut_plane() void GLGizmoCut3D::render_cut_center_graber() { + ::glDisable(GL_DEPTH_TEST); + Slic3r::ScopeGuard guard([]() { ::glEnable(GL_DEPTH_TEST); }); const Vec3d& angles = m_rotation_gizmo.get_rotation(); m_grabbers[0].angles = angles; m_grabbers[0].color = GRABBER_COLOR; @@ -878,9 +882,13 @@ void GLGizmoCut3D::on_render() render_connectors(false); + if (! m_connectors_editing) + ::glDisable(GL_DEPTH_TEST); m_c->object_clipper()->render_cut(); + if (! m_connectors_editing) + ::glEnable(GL_DEPTH_TEST); - if (!m_hide_cut_plane) { + if (!m_hide_cut_plane && ! m_connectors_editing) { render_cut_center_graber(); render_cut_plane(); if (m_hover_id < m_group_id && m_mode == size_t(CutMode::cutPlanar) && !cut_line_processing()) @@ -922,146 +930,175 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) last_y = y; } - render_combo(_u8L("Mode"), m_modes, m_mode); - - bool revert_rotation{ false }; - bool revert_move{ false }; + // render_combo(_u8L("Mode"), m_modes, m_mode); CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + bool cut_clicked = false; + bool revert_move{ false }; + bool revert_rotation{ false }; - if (m_mode == size_t(CutMode::cutPlanar)) { - ImGui::AlignTextToFramePadding(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); - ImGui::Separator(); + if (! m_connectors_editing) { + if (m_mode == size_t(CutMode::cutPlanar)) { + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); + ImGui::Separator(); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Move center")); + //////// + double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); + const BoundingBoxf3 tbb = transformed_bounding_box(); + double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; + double bottom = (tbb.max.z() <= 0.0 ? tbb.size().z() : (tbb.min.z() * (-1))) * koef; + + //static float v = 0.; // TODO: connect to cutting plane position + m_imgui->text(_L("Cut position: ")); + render_move_center_input(Z); + //m_imgui->input_double(unit_str, v); + //v = std::clamp(v, 0.f, float(bottom+top)); + if (m_imgui->button("Reset cutting plane")) { + // TODO: reset both position and rotation + } + ////// - m_imgui->disabled_begin(m_plane_center == bounding_box().center()); - revert_move = render_revert_button("move"); - m_imgui->disabled_end(); + // ImGui::AlignTextToFramePadding(); + // m_imgui->text(_L("Move center")); - for (Axis axis : {X, Y, Z}) - render_move_center_input(axis); - m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + // m_imgui->disabled_begin(m_plane_center == bounding_box().center()); + // revert_move = render_revert_button("move"); + // m_imgui->disabled_end(); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Rotation")); + // for (Axis axis : {X, Y, Z}) + // render_move_center_input(axis); + // m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); - m_imgui->disabled_begin(m_rotation_gizmo.get_rotation() == Vec3d::Zero()); - revert_rotation = render_revert_button("rotation"); - m_imgui->disabled_end(); + // ImGui::AlignTextToFramePadding(); + // m_imgui->text(_L("Rotation")); - for (Axis axis : {X, Y, Z}) - render_rotation_input(axis); - m_imgui->text(_L("°")); + // m_imgui->disabled_begin(m_rotation_gizmo.get_rotation() == Vec3d::Zero()); + // revert_rotation = render_revert_button("rotation"); + // m_imgui->disabled_end(); + + // for (Axis axis : {X, Y, Z}) + // render_rotation_input(axis); + // m_imgui->text(_L("°")); + + // ImGui::Separator(); + + // double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + // wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); + + // Vec3d tbb_sz = transformed_bounding_box().size(); + // wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + + // ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + + // ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + + // ImGui::AlignTextToFramePadding(); + // m_imgui->text(_L("Build size")); + // ImGui::SameLine(m_label_width); + // m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); + } + + if (m_mode == size_t(CutMode::cutPlanar)) { + ImGui::Separator(); + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("After cut") + ": "); + bool keep = true; + + ImGui::SameLine(m_label_width); + m_imgui->text(_L("Upper part")); + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(!m_keep_upper); + m_imgui->disabled_begin(is_approx(m_rotation_gizmo.get_rotation().x(), 0.) && is_approx(m_rotation_gizmo.get_rotation().y(), 0.)); + m_imgui->checkbox(_L("Place on cut") + "##upper", m_rotate_upper); + m_imgui->disabled_end(); + m_imgui->disabled_end(); + + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->text(_L("Lower part")); + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); + m_imgui->disabled_end(); + ImGui::SameLine(); + m_imgui->disabled_begin(!m_keep_lower); + m_imgui->checkbox(_L("Place on cut") + "##lower", m_rotate_lower); + m_imgui->disabled_end(); + } ImGui::Separator(); - double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); - - Vec3d tbb_sz = transformed_bounding_box().size(); - wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + - ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + - ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Build size")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); - } - - m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); - // Connectors section - ImGui::Separator(); - - ImGui::AlignTextToFramePadding(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); - - m_imgui->disabled_begin(connectors.empty()); - ImGui::SameLine(m_label_width); - if (m_imgui->button(" " + _L("Reset") + " ##connectors")) - reset_connectors(); - m_imgui->disabled_end(); - - m_imgui->text(_L("Mode")); - render_connect_mode_radio_button(CutConnectorMode::Auto); - render_connect_mode_radio_button(CutConnectorMode::Manual); - - m_imgui->text(_L("Type")); - render_connect_type_radio_button(CutConnectorType::Plug); - render_connect_type_radio_button(CutConnectorType::Dowel); - - if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) - update_connector_shape(); - if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) - update_connector_shape(); - - if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) - for (auto& connector : connectors) - connector.height = float(m_connector_depth_ratio); - if (render_double_input(_u8L("Size"), m_connector_size)) - for (auto& connector : connectors) - connector.radius = float(m_connector_size * 0.5); - - m_imgui->disabled_end(); - - if (m_mode == size_t(CutMode::cutPlanar)) { + if (m_imgui->button(_L("Add/Edit connectors"))) + m_connectors_editing = true; + } else { // connectors mode + m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); + // Connectors section ImGui::Separator(); ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut") + ": "); - bool keep = true; + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); + m_imgui->disabled_begin(connectors.empty()); ImGui::SameLine(m_label_width); - m_imgui->text(_L("Upper part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); - m_imgui->disabled_end(); - ImGui::SameLine(); - m_imgui->disabled_begin(!m_keep_upper); - m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper); + if (m_imgui->button(" " + _L("Reset") + " ##connectors")) + reset_connectors(); m_imgui->disabled_end(); - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Lower part")); - ImGui::SameLine(2 * m_label_width); + m_imgui->text(_L("Mode")); + render_connect_mode_radio_button(CutConnectorMode::Auto); + render_connect_mode_radio_button(CutConnectorMode::Manual); + + m_imgui->text(_L("Type")); + render_connect_type_radio_button(CutConnectorType::Plug); + render_connect_type_radio_button(CutConnectorType::Dowel); + + if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) + update_connector_shape(); + if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) + update_connector_shape(); + + if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) + for (auto& connector : connectors) + connector.height = float(m_connector_depth_ratio); + if (render_double_input(_u8L("Size"), m_connector_size)) + for (auto& connector : connectors) + connector.radius = float(m_connector_size * 0.5); - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); - m_imgui->disabled_end(); - ImGui::SameLine(); - m_imgui->disabled_begin(!m_keep_lower); - m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower); m_imgui->disabled_end(); + + if (m_imgui->button(_L("Confirm connectors"))) + m_connectors_editing = false; } ImGui::Separator(); + m_imgui->text(m_has_invalid_connector ? wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.") : wxString()); m_imgui->disabled_begin(!can_perform_cut()); - const bool cut_clicked = m_imgui->button(_L("Perform cut")); + cut_clicked = m_imgui->button(_L("Perform cut")); m_imgui->disabled_end(); - ImGui::Separator(); - - m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); + m_imgui->end(); //////// - static bool hide_clipped = true; - static bool fill_cut = true; - static float contour_width = 0.2f; + m_imgui->begin(wxString("DEBUG")); + static bool hide_clipped = false; + static bool fill_cut = false; + static float contour_width = 0.4f; + m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); if (m_imgui->checkbox("hide_clipped", hide_clipped) && !hide_clipped) m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_imgui->checkbox("fill_cut", fill_cut); m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); - m_c->object_clipper()->set_behavior(hide_clipped, fill_cut, contour_width); - //////// - + m_c->object_clipper()->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, contour_width); m_imgui->end(); + //////// if (cut_clicked && (m_keep_upper || m_keep_lower)) perform_cut(m_parent.get_selection()); @@ -1101,10 +1138,16 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c void GLGizmoCut3D::render_connectors(bool picking) { - if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) + if (picking && ! m_connectors_editing) return; - m_has_invalid_connector = false; + const bool depth_test = m_connectors_editing; + if (! depth_test) + ::glDisable(GL_DEPTH_TEST); + Slic3r::ScopeGuard guard_depth_test([&](){ if (! depth_test) ::glEnable(GL_DEPTH_TEST); }); + + if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) + return; const ModelObject* mo = m_c->selection_info()->model_object(); auto inst_id = m_c->selection_info()->get_active_instance(); @@ -1150,6 +1193,8 @@ void GLGizmoCut3D::render_connectors(bool picking) const Transform3d instance_trafo = Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix(); + m_has_invalid_connector = false; + for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; // const bool& point_selected = m_selected[i]; @@ -1180,13 +1225,12 @@ void GLGizmoCut3D::render_connectors(bool picking) const Transform3d volume_trafo = get_volume_transformation(mv); if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { - render_color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(0.5f, 0.5f, 0.5f, 1.f); break; } render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); - } - if (!m_has_invalid_connector && render_color == ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f)) m_has_invalid_connector = true; + } } } @@ -1330,7 +1374,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair const Transform3d volume_trafo = get_volume_transformation(mv); m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * volume_trafo, - camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + camera, hit, normal, m_c->object_clipper()->get_clipping_plane(true), nullptr, &clipping_plane_was_hit); if (clipping_plane_was_hit) { // recalculate hit to object's local position @@ -1431,14 +1475,14 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) return false; - if ( m_hover_id < 0 && shift_down && + if ( m_hover_id < 0 && shift_down && ! m_connectors_editing && (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::Moving) ) return process_cut_line(action, mouse_position); CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; // left down without selection rectangle - place connector on the cut plane: - if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down) { + if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down && m_connectors_editing) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id != -1) return false; @@ -1465,7 +1509,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi } return true; } - else if (action == SLAGizmoEventType::RightDown && !shift_down) { + else if (action == SLAGizmoEventType::RightDown && !shift_down && m_connectors_editing) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id < m_connectors_group_id) return false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 439abea8d..dfaef3fb2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -57,6 +57,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_rotate_lower{ false }; bool m_hide_cut_plane{ false }; + bool m_connectors_editing{ false }; double m_connector_depth_ratio{ 3.0 }; double m_connector_size{ 2.5 }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index e7ba37010..c28fe6356 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -475,10 +475,10 @@ void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset get_pool()->get_canvas()->set_as_dirty(); } -const ClippingPlane* ObjectClipper::get_clipping_plane() const +const ClippingPlane* ObjectClipper::get_clipping_plane(bool ignore_hide_clipped) const { static const ClippingPlane no_clip = ClippingPlane::ClipsNothing(); - return m_hide_clipped ? m_clp.get() : &no_clip; + return (ignore_hide_clipped || m_hide_clipped) ? m_clp.get() : &no_clip; } void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contour_width) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index efd3b436e..72df620cf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -257,7 +257,7 @@ public: void set_normal(const Vec3d& dir); double get_position() const { return m_clp_ratio; } - const ClippingPlane* get_clipping_plane() const; + const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const; void render_cut() const; void set_position_by_ratio(double pos, bool keep_normal); void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 65b292c25..ff084b0a4 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -76,6 +76,7 @@ static const std::map font_icons = { {ImGui::LegendToolMarker , "legend_toolmarker" }, #endif // ENABLE_LEGEND_TOOLBAR_ICONS {ImGui::RevertButton , "undo" }, + {ImGui::WarningMarkerSmall , "notification_warning" }, }; static const std::map font_icons_large = { From 76ea74c28921a4831ed5ae14b22f5b3f17c1184f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jun 2022 12:25:01 +0200 Subject: [PATCH 38/93] Update wxWidgets to 3.1.7 added handling for nanosvg with cmake --- deps/CMakeLists.txt | 1 + deps/NanoSVG/NanoSVG.cmake | 4 + deps/wxWidgets/wxWidgets.cmake | 14 +- src/CMakeLists.txt | 5 +- src/nanosvg/README-prusa.txt | 1 - src/nanosvg/nanosvg.h | 2979 ------------------------------- src/nanosvg/nanosvgrast.h | 1452 --------------- src/slic3r/CMakeLists.txt | 4 +- src/slic3r/GUI/BitmapCache.cpp | 6 +- src/slic3r/GUI/GLTexture.cpp | 4 +- src/slic3r/GUI/ImGuiWrapper.cpp | 5 +- 11 files changed, 24 insertions(+), 4451 deletions(-) create mode 100644 deps/NanoSVG/NanoSVG.cmake delete mode 100644 src/nanosvg/README-prusa.txt delete mode 100644 src/nanosvg/nanosvg.h delete mode 100644 src/nanosvg/nanosvgrast.h diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index d129ff1c2..eb0c420fa 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -188,6 +188,7 @@ endif () include(JPEG/JPEG.cmake) include(TIFF/TIFF.cmake) +include(NanoSVG/NanoSVG.cmake) include(wxWidgets/wxWidgets.cmake) set(_dep_list diff --git a/deps/NanoSVG/NanoSVG.cmake b/deps/NanoSVG/NanoSVG.cmake new file mode 100644 index 000000000..9623d3226 --- /dev/null +++ b/deps/NanoSVG/NanoSVG.cmake @@ -0,0 +1,4 @@ +prusaslicer_add_cmake_project(NanoSVG + URL https://github.com/memononen/nanosvg/archive/4c8f0139b62c6e7faa3b67ce1fbe6e63590ed148.zip + URL_HASH SHA256=584e084af1a75bf633f79753ce2f6f6ec8686002ca27f35f1037c25675fecfb6 +) \ No newline at end of file diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index bf5fd6289..4a0875d62 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -1,5 +1,3 @@ -set(_wx_git_tag v3.1.4-patched) - set(_wx_toolkit "") if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(_gtk_ver 2) @@ -15,11 +13,9 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for endif() prusaslicer_add_cmake_project(wxWidgets - # GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" - # GIT_TAG tm_cross_compile #${_wx_git_tag} - URL https://github.com/prusa3d/wxWidgets/archive/489f6118256853cf5b299d595868641938566cdb.zip - URL_HASH SHA256=5b22d465377cedd8044bba69bea958b248953fd3628c1de4913a84d4e6f6175b - DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG + URL https://github.com/prusa3d/wxWidgets/archive/5412ac15586da3ecb6952fcc875d2a23366c998f.zip + URL_HASH SHA256=85a6e13152289fbf1ea51f221fbe1452e7914bbaa665b89536780810e93948a6 + DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG CMAKE_ARGS -DwxBUILD_PRECOMP=ON ${_wx_toolkit} @@ -32,7 +28,9 @@ prusaslicer_add_cmake_project(wxWidgets -DwxUSE_OPENGL=ON -DwxUSE_LIBPNG=sys -DwxUSE_ZLIB=sys - -DwxUSE_REGEX=builtin + -DwxUSE_NANOSVG=sys + -DwxUSE_NANOSVG_EXTERNAL=ON + -DwxUSE_REGEX=OFF -DwxUSE_LIBXPM=builtin -DwxUSE_LIBJPEG=sys -DwxUSE_LIBTIFF=sys diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 801760b8c..8a093f639 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -105,7 +105,10 @@ if (SLIC3R_GUI) # wrong libs for opengl in the link line and it does not link to it by himself. # libslic3r_gui will link to opengl anyway, so lets override wx list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL) - + + if (UNIX AND NOT APPLE) + list(APPEND wxWidgets_LIBRARIES X11 wayland-client wayland-egl EGL) + endif () # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") diff --git a/src/nanosvg/README-prusa.txt b/src/nanosvg/README-prusa.txt deleted file mode 100644 index 8388aa8ef..000000000 --- a/src/nanosvg/README-prusa.txt +++ /dev/null @@ -1 +0,0 @@ -Upstream source: https://github.com/memononen/nanosvg/tree/c1f6e209c16b18b46aa9f45d7e619acf42c29726 \ No newline at end of file diff --git a/src/nanosvg/nanosvg.h b/src/nanosvg/nanosvg.h deleted file mode 100644 index 57bcb7c2c..000000000 --- a/src/nanosvg/nanosvg.h +++ /dev/null @@ -1,2979 +0,0 @@ -/* - * Copyright (c) 2013-14 Mikko Mononen memon@inside.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example - * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) - * - * Arc calculation code based on canvg (https://code.google.com/p/canvg/) - * - * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html - * - */ - -#ifndef NANOSVG_H -#define NANOSVG_H - -#ifndef NANOSVG_CPLUSPLUS -#ifdef __cplusplus -extern "C" { -#endif -#endif - -// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. -// -// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. -// -// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! -// -// The shapes in the SVG images are transformed by the viewBox and converted to specified units. -// That is, you should get the same looking data as your designed in your favorite app. -// -// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose -// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. -// -// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. -// DPI (dots-per-inch) controls how the unit conversion is done. -// -// If you don't know or care about the units stuff, "px" and 96 should get you going. - - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - printf("size: %f x %f\n", image->width, image->height); - // Use... - for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { - for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { - for (int i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); - } - } - } - // Delete - nsvgDelete(image); -*/ - -enum NSVGpaintType { - NSVG_PAINT_NONE = 0, - NSVG_PAINT_COLOR = 1, - NSVG_PAINT_LINEAR_GRADIENT = 2, - NSVG_PAINT_RADIAL_GRADIENT = 3 -}; - -enum NSVGspreadType { - NSVG_SPREAD_PAD = 0, - NSVG_SPREAD_REFLECT = 1, - NSVG_SPREAD_REPEAT = 2 -}; - -enum NSVGlineJoin { - NSVG_JOIN_MITER = 0, - NSVG_JOIN_ROUND = 1, - NSVG_JOIN_BEVEL = 2 -}; - -enum NSVGlineCap { - NSVG_CAP_BUTT = 0, - NSVG_CAP_ROUND = 1, - NSVG_CAP_SQUARE = 2 -}; - -enum NSVGfillRule { - NSVG_FILLRULE_NONZERO = 0, - NSVG_FILLRULE_EVENODD = 1 -}; - -enum NSVGflags { - NSVG_FLAGS_VISIBLE = 0x01 -}; - -typedef struct NSVGgradientStop { - unsigned int color; - float offset; -} NSVGgradientStop; - -typedef struct NSVGgradient { - float xform[6]; - char spread; - float fx, fy; - int nstops; - NSVGgradientStop stops[1]; -} NSVGgradient; - -typedef struct NSVGpaint { - char type; - union { - unsigned int color; - NSVGgradient* gradient; - }; -} NSVGpaint; - -typedef struct NSVGpath -{ - float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... - int npts; // Total number of bezier points. - char closed; // Flag indicating if shapes should be treated as closed. - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - struct NSVGpath* next; // Pointer to next path, or NULL if last element. -} NSVGpath; - -typedef struct NSVGshape -{ - char id[64]; // Optional 'id' attr of the shape or its group - NSVGpaint fill; // Fill paint - NSVGpaint stroke; // Stroke paint - float opacity; // Opacity of the shape. - float strokeWidth; // Stroke width (scaled). - float strokeDashOffset; // Stroke dash offset (scaled). - float strokeDashArray[8]; // Stroke dash array (scaled). - char strokeDashCount; // Number of dash values in dash array. - char strokeLineJoin; // Stroke join type. - char strokeLineCap; // Stroke cap type. - float miterLimit; // Miter limit - char fillRule; // Fill rule, see NSVGfillRule. - unsigned char flags; // Logical or of NSVG_FLAGS_* flags - float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - NSVGpath* paths; // Linked list of paths in the image. - struct NSVGshape* next; // Pointer to next shape, or NULL if last element. -} NSVGshape; - -typedef struct NSVGimage -{ - float width; // Width of the image. - float height; // Height of the image. - NSVGshape* shapes; // Linked list of shapes in the image. -} NSVGimage; - -// Parses SVG file from a file, returns SVG image as paths. -NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); - -// Parses SVG file from a null terminated string, returns SVG image as paths. -// Important note: changes the string. -NSVGimage* nsvgParse(char* input, const char* units, float dpi); - -// Duplicates a path. -NSVGpath* nsvgDuplicatePath(NSVGpath* p); - -// Deletes an image. -void nsvgDelete(NSVGimage* image); - -#ifndef NANOSVG_CPLUSPLUS -#ifdef __cplusplus -} -#endif -#endif - -#endif // NANOSVG_H - -#ifdef NANOSVG_IMPLEMENTATION - -#include -#include -#include - -#include - -#define NSVG_PI (3.14159265358979323846264338327f) -#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. - -#define NSVG_ALIGN_MIN 0 -#define NSVG_ALIGN_MID 1 -#define NSVG_ALIGN_MAX 2 -#define NSVG_ALIGN_NONE 0 -#define NSVG_ALIGN_MEET 1 -#define NSVG_ALIGN_SLICE 2 - -#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) -#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) - -#ifdef _MSC_VER - #pragma warning (disable: 4996) // Switch off security warnings - #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings - #ifdef __cplusplus - #define NSVG_INLINE inline - #else - #define NSVG_INLINE - #endif -#else - #define NSVG_INLINE inline -#endif - - -static int nsvg__isspace(char c) -{ - return strchr(" \t\n\v\f\r", c) != 0; -} - -static int nsvg__isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int nsvg__isnum(char c) -{ - return strchr("0123456789+-.eE", c) != 0; -} - -static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } -static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } - - -// Simple XML parser - -#define NSVG_XML_TAG 1 -#define NSVG_XML_CONTENT 2 -#define NSVG_XML_MAX_ATTRIBS 256 - -static void nsvg__parseContent(char* s, - void (*contentCb)(void* ud, const char* s), - void* ud) -{ - // Trim start white spaces - while (*s && nsvg__isspace(*s)) s++; - if (!*s) return; - - if (contentCb) - (*contentCb)(ud, s); -} - -static void nsvg__parseElement(char* s, - void (*startelCb)(void* ud, const char* el, const char** attr), - void (*endelCb)(void* ud, const char* el), - void* ud) -{ - const char* attr[NSVG_XML_MAX_ATTRIBS]; - int nattr = 0; - char* name; - int start = 0; - int end = 0; - char quote; - - // Skip white space after the '<' - while (*s && nsvg__isspace(*s)) s++; - - // Check if the tag is end tag - if (*s == '/') { - s++; - end = 1; - } else { - start = 1; - } - - // Skip comments, data and preprocessor stuff. - if (!*s || *s == '?' || *s == '!') - return; - - // Get tag name - name = s; - while (*s && !nsvg__isspace(*s)) s++; - if (*s) { *s++ = '\0'; } - - // Get attribs - while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { - char* name = NULL; - char* value = NULL; - - // Skip white space before the attrib name - while (*s && nsvg__isspace(*s)) s++; - if (!*s) break; - if (*s == '/') { - end = 1; - break; - } - name = s; - // Find end of the attrib name. - while (*s && !nsvg__isspace(*s) && *s != '=') s++; - if (*s) { *s++ = '\0'; } - // Skip until the beginning of the value. - while (*s && *s != '\"' && *s != '\'') s++; - if (!*s) break; - quote = *s; - s++; - // Store value and find the end of it. - value = s; - while (*s && *s != quote) s++; - if (*s) { *s++ = '\0'; } - - // Store only well formed attributes - if (name && value) { - attr[nattr++] = name; - attr[nattr++] = value; - } - } - - // List terminator - attr[nattr++] = 0; - attr[nattr++] = 0; - - // Call callbacks. - if (start && startelCb) - (*startelCb)(ud, name, attr); - if (end && endelCb) - (*endelCb)(ud, name); -} - -int nsvg__parseXML(char* input, - void (*startelCb)(void* ud, const char* el, const char** attr), - void (*endelCb)(void* ud, const char* el), - void (*contentCb)(void* ud, const char* s), - void* ud) -{ - char* s = input; - char* mark = s; - int state = NSVG_XML_CONTENT; - while (*s) { - if (*s == '<' && state == NSVG_XML_CONTENT) { - // Start of a tag - *s++ = '\0'; - nsvg__parseContent(mark, contentCb, ud); - mark = s; - state = NSVG_XML_TAG; - } else if (*s == '>' && state == NSVG_XML_TAG) { - // Start of a content or new tag. - *s++ = '\0'; - nsvg__parseElement(mark, startelCb, endelCb, ud); - mark = s; - state = NSVG_XML_CONTENT; - } else { - s++; - } - } - - return 1; -} - - -/* Simple SVG parser. */ - -#define NSVG_MAX_ATTR 128 - -enum NSVGgradientUnits { - NSVG_USER_SPACE = 0, - NSVG_OBJECT_SPACE = 1 -}; - -#define NSVG_MAX_DASHES 8 - -enum NSVGunits { - NSVG_UNITS_USER, - NSVG_UNITS_PX, - NSVG_UNITS_PT, - NSVG_UNITS_PC, - NSVG_UNITS_MM, - NSVG_UNITS_CM, - NSVG_UNITS_IN, - NSVG_UNITS_PERCENT, - NSVG_UNITS_EM, - NSVG_UNITS_EX -}; - -typedef struct NSVGcoordinate { - float value; - int units; -} NSVGcoordinate; - -typedef struct NSVGlinearData { - NSVGcoordinate x1, y1, x2, y2; -} NSVGlinearData; - -typedef struct NSVGradialData { - NSVGcoordinate cx, cy, r, fx, fy; -} NSVGradialData; - -typedef struct NSVGgradientData -{ - char id[64]; - char ref[64]; - char type; - union { - NSVGlinearData linear; - NSVGradialData radial; - }; - char spread; - char units; - float xform[6]; - int nstops; - NSVGgradientStop* stops; - struct NSVGgradientData* next; -} NSVGgradientData; - -typedef struct NSVGattrib -{ - char id[64]; - float xform[6]; - unsigned int fillColor; - unsigned int strokeColor; - float opacity; - float fillOpacity; - float strokeOpacity; - char fillGradient[64]; - char strokeGradient[64]; - float strokeWidth; - float strokeDashOffset; - float strokeDashArray[NSVG_MAX_DASHES]; - int strokeDashCount; - char strokeLineJoin; - char strokeLineCap; - float miterLimit; - char fillRule; - float fontSize; - unsigned int stopColor; - float stopOpacity; - float stopOffset; - char hasFill; - char hasStroke; - char visible; -} NSVGattrib; - -typedef struct NSVGparser -{ - NSVGattrib attr[NSVG_MAX_ATTR]; - int attrHead; - float* pts; - int npts; - int cpts; - NSVGpath* plist; - NSVGimage* image; - NSVGgradientData* gradients; - NSVGshape* shapesTail; - float viewMinx, viewMiny, viewWidth, viewHeight; - int alignX, alignY, alignType; - float dpi; - char pathFlag; - char defsFlag; -} NSVGparser; - -static void nsvg__xformIdentity(float* t) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetTranslation(float* t, float tx, float ty) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = 0.0f; t[3] = 1.0f; - t[4] = tx; t[5] = ty; -} - -static void nsvg__xformSetScale(float* t, float sx, float sy) -{ - t[0] = sx; t[1] = 0.0f; - t[2] = 0.0f; t[3] = sy; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetSkewX(float* t, float a) -{ - t[0] = 1.0f; t[1] = 0.0f; - t[2] = tanf(a); t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetSkewY(float* t, float a) -{ - t[0] = 1.0f; t[1] = tanf(a); - t[2] = 0.0f; t[3] = 1.0f; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformSetRotation(float* t, float a) -{ - float cs = cosf(a), sn = sinf(a); - t[0] = cs; t[1] = sn; - t[2] = -sn; t[3] = cs; - t[4] = 0.0f; t[5] = 0.0f; -} - -static void nsvg__xformMultiply(float* t, float* s) -{ - float t0 = t[0] * s[0] + t[1] * s[2]; - float t2 = t[2] * s[0] + t[3] * s[2]; - float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; - t[1] = t[0] * s[1] + t[1] * s[3]; - t[3] = t[2] * s[1] + t[3] * s[3]; - t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; - t[0] = t0; - t[2] = t2; - t[4] = t4; -} - -static void nsvg__xformInverse(float* inv, float* t) -{ - double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; - if (det > -1e-6 && det < 1e-6) { - nsvg__xformIdentity(t); - return; - } - invdet = 1.0 / det; - inv[0] = (float)(t[3] * invdet); - inv[2] = (float)(-t[2] * invdet); - inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); - inv[1] = (float)(-t[1] * invdet); - inv[3] = (float)(t[0] * invdet); - inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); -} - -static void nsvg__xformPremultiply(float* t, float* s) -{ - float s2[6]; - memcpy(s2, s, sizeof(float)*6); - nsvg__xformMultiply(s2, t); - memcpy(t, s2, sizeof(float)*6); -} - -static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) -{ - *dx = x*t[0] + y*t[2] + t[4]; - *dy = x*t[1] + y*t[3] + t[5]; -} - -static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) -{ - *dx = x*t[0] + y*t[2]; - *dy = x*t[1] + y*t[3]; -} - -#define NSVG_EPSILON (1e-12) - -static int nsvg__ptInBounds(float* pt, float* bounds) -{ - return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; -} - - -static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) -{ - double it = 1.0-t; - return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; -} - -static void nsvg__curveBounds(float* bounds, float* curve) -{ - int i, j, count; - double roots[2], a, b, c, b2ac, t, v; - float* v0 = &curve[0]; - float* v1 = &curve[2]; - float* v2 = &curve[4]; - float* v3 = &curve[6]; - - // Start the bounding box by end points - bounds[0] = nsvg__minf(v0[0], v3[0]); - bounds[1] = nsvg__minf(v0[1], v3[1]); - bounds[2] = nsvg__maxf(v0[0], v3[0]); - bounds[3] = nsvg__maxf(v0[1], v3[1]); - - // Bezier curve fits inside the convex hull of it's control points. - // If control points are inside the bounds, we're done. - if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) - return; - - // Add bezier curve inflection points in X and Y. - for (i = 0; i < 2; i++) { - a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; - b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; - c = 3.0 * v1[i] - 3.0 * v0[i]; - count = 0; - if (fabs(a) < NSVG_EPSILON) { - if (fabs(b) > NSVG_EPSILON) { - t = -c / b; - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - } - } else { - b2ac = b*b - 4.0*c*a; - if (b2ac > NSVG_EPSILON) { - t = (-b + sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - t = (-b - sqrt(b2ac)) / (2.0 * a); - if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) - roots[count++] = t; - } - } - for (j = 0; j < count; j++) { - v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); - bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); - bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); - } - } -} - -static NSVGparser* nsvg__createParser() -{ - NSVGparser* p; - p = (NSVGparser*)malloc(sizeof(NSVGparser)); - if (p == NULL) goto error; - memset(p, 0, sizeof(NSVGparser)); - - p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); - if (p->image == NULL) goto error; - memset(p->image, 0, sizeof(NSVGimage)); - - // Init style - nsvg__xformIdentity(p->attr[0].xform); - memset(p->attr[0].id, 0, sizeof p->attr[0].id); - p->attr[0].fillColor = NSVG_RGB(0,0,0); - p->attr[0].strokeColor = NSVG_RGB(0,0,0); - p->attr[0].opacity = 1; - p->attr[0].fillOpacity = 1; - p->attr[0].strokeOpacity = 1; - p->attr[0].stopOpacity = 1; - p->attr[0].strokeWidth = 1; - p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; - p->attr[0].strokeLineCap = NSVG_CAP_BUTT; - p->attr[0].miterLimit = 4; - p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; - p->attr[0].hasFill = 1; - p->attr[0].visible = 1; - - return p; - -error: - if (p) { - if (p->image) free(p->image); - free(p); - } - return NULL; -} - -static void nsvg__deletePaths(NSVGpath* path) -{ - while (path) { - NSVGpath *next = path->next; - if (path->pts != NULL) - free(path->pts); - free(path); - path = next; - } -} - -static void nsvg__deletePaint(NSVGpaint* paint) -{ - if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) - free(paint->gradient); -} - -static void nsvg__deleteGradientData(NSVGgradientData* grad) -{ - NSVGgradientData* next; - while (grad != NULL) { - next = grad->next; - free(grad->stops); - free(grad); - grad = next; - } -} - -static void nsvg__deleteParser(NSVGparser* p) -{ - if (p != NULL) { - nsvg__deletePaths(p->plist); - nsvg__deleteGradientData(p->gradients); - nsvgDelete(p->image); - free(p->pts); - free(p); - } -} - -static void nsvg__resetPath(NSVGparser* p) -{ - p->npts = 0; -} - -static void nsvg__addPoint(NSVGparser* p, float x, float y) -{ - if (p->npts+1 > p->cpts) { - p->cpts = p->cpts ? p->cpts*2 : 8; - p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); - if (!p->pts) return; - } - p->pts[p->npts*2+0] = x; - p->pts[p->npts*2+1] = y; - p->npts++; -} - -static void nsvg__moveTo(NSVGparser* p, float x, float y) -{ - if (p->npts > 0) { - p->pts[(p->npts-1)*2+0] = x; - p->pts[(p->npts-1)*2+1] = y; - } else { - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__lineTo(NSVGparser* p, float x, float y) -{ - float px,py, dx,dy; - if (p->npts > 0) { - px = p->pts[(p->npts-1)*2+0]; - py = p->pts[(p->npts-1)*2+1]; - dx = x - px; - dy = y - py; - nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); - nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); - nsvg__addPoint(p, x, y); - } -} - -static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) -{ - nsvg__addPoint(p, cpx1, cpy1); - nsvg__addPoint(p, cpx2, cpy2); - nsvg__addPoint(p, x, y); -} - -static NSVGattrib* nsvg__getAttr(NSVGparser* p) -{ - return &p->attr[p->attrHead]; -} - -static void nsvg__pushAttr(NSVGparser* p) -{ - if (p->attrHead < NSVG_MAX_ATTR-1) { - p->attrHead++; - memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); - } -} - -static void nsvg__popAttr(NSVGparser* p) -{ - if (p->attrHead > 0) - p->attrHead--; -} - -static float nsvg__actualOrigX(NSVGparser* p) -{ - return p->viewMinx; -} - -static float nsvg__actualOrigY(NSVGparser* p) -{ - return p->viewMiny; -} - -static float nsvg__actualWidth(NSVGparser* p) -{ - return p->viewWidth; -} - -static float nsvg__actualHeight(NSVGparser* p) -{ - return p->viewHeight; -} - -static float nsvg__actualLength(NSVGparser* p) -{ - float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); - return sqrtf(w*w + h*h) / sqrtf(2.0f); -} - -static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) -{ - NSVGattrib* attr = nsvg__getAttr(p); - switch (c.units) { - case NSVG_UNITS_USER: return c.value; - case NSVG_UNITS_PX: return c.value; - case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; - case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; - case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; - case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; - case NSVG_UNITS_IN: return c.value * p->dpi; - case NSVG_UNITS_EM: return c.value * attr->fontSize; - case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. - case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; - default: return c.value; - } - return c.value; -} - -static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) -{ - NSVGgradientData* grad = p->gradients; - while (grad) { - if (strcmp(grad->id, id) == 0) - return grad; - grad = grad->next; - } - return NULL; -} - -static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) -{ - NSVGattrib* attr = nsvg__getAttr(p); - NSVGgradientData* data = NULL; - NSVGgradientData* ref = NULL; - NSVGgradientStop* stops = NULL; - NSVGgradient* grad; - float ox, oy, sw, sh, sl; - int nstops = 0; - - data = nsvg__findGradientData(p, id); - if (data == NULL) return NULL; - - // TODO: use ref to fill in all unset values too. - ref = data; - while (ref != NULL) { - if (stops == NULL && ref->stops != NULL) { - stops = ref->stops; - nstops = ref->nstops; - break; - } - ref = nsvg__findGradientData(p, ref->ref); - } - if (stops == NULL) return NULL; - - grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); - if (grad == NULL) return NULL; - - // The shape width and height. - if (data->units == NSVG_OBJECT_SPACE) { - ox = localBounds[0]; - oy = localBounds[1]; - sw = localBounds[2] - localBounds[0]; - sh = localBounds[3] - localBounds[1]; - } else { - ox = nsvg__actualOrigX(p); - oy = nsvg__actualOrigY(p); - sw = nsvg__actualWidth(p); - sh = nsvg__actualHeight(p); - } - sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); - - if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { - float x1, y1, x2, y2, dx, dy; - x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); - y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); - x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); - y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); - // Calculate transform aligned to the line - dx = x2 - x1; - dy = y2 - y1; - grad->xform[0] = dy; grad->xform[1] = -dx; - grad->xform[2] = dx; grad->xform[3] = dy; - grad->xform[4] = x1; grad->xform[5] = y1; - } else { - float cx, cy, fx, fy, r; - cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); - cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); - fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); - fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); - r = nsvg__convertToPixels(p, data->radial.r, 0, sl); - // Calculate transform aligned to the circle - grad->xform[0] = r; grad->xform[1] = 0; - grad->xform[2] = 0; grad->xform[3] = r; - grad->xform[4] = cx; grad->xform[5] = cy; - grad->fx = fx / r; - grad->fy = fy / r; - } - - nsvg__xformMultiply(grad->xform, data->xform); - nsvg__xformMultiply(grad->xform, attr->xform); - - grad->spread = data->spread; - memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); - grad->nstops = nstops; - - *paintType = data->type; - - return grad; -} - -static float nsvg__getAverageScale(float* t) -{ - float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); - float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); - return (sx + sy) * 0.5f; -} - -static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) -{ - NSVGpath* path; - float curve[4*2], curveBounds[4]; - int i, first = 1; - for (path = shape->paths; path != NULL; path = path->next) { - nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); - for (i = 0; i < path->npts-1; i += 3) { - nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); - nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); - nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); - nsvg__curveBounds(curveBounds, curve); - if (first) { - bounds[0] = curveBounds[0]; - bounds[1] = curveBounds[1]; - bounds[2] = curveBounds[2]; - bounds[3] = curveBounds[3]; - first = 0; - } else { - bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); - bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); - bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); - bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); - } - curve[0] = curve[6]; - curve[1] = curve[7]; - } - } -} - -static void nsvg__addShape(NSVGparser* p) -{ - NSVGattrib* attr = nsvg__getAttr(p); - float scale = 1.0f; - NSVGshape* shape; - NSVGpath* path; - int i; - - if (p->plist == NULL) - return; - - shape = (NSVGshape*)malloc(sizeof(NSVGshape)); - if (shape == NULL) goto error; - memset(shape, 0, sizeof(NSVGshape)); - - memcpy(shape->id, attr->id, sizeof shape->id); - scale = nsvg__getAverageScale(attr->xform); - shape->strokeWidth = attr->strokeWidth * scale; - shape->strokeDashOffset = attr->strokeDashOffset * scale; - shape->strokeDashCount = (char)attr->strokeDashCount; - for (i = 0; i < attr->strokeDashCount; i++) - shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; - shape->strokeLineJoin = attr->strokeLineJoin; - shape->strokeLineCap = attr->strokeLineCap; - shape->miterLimit = attr->miterLimit; - shape->fillRule = attr->fillRule; - shape->opacity = attr->opacity; - - shape->paths = p->plist; - p->plist = NULL; - - // Calculate shape bounds - shape->bounds[0] = shape->paths->bounds[0]; - shape->bounds[1] = shape->paths->bounds[1]; - shape->bounds[2] = shape->paths->bounds[2]; - shape->bounds[3] = shape->paths->bounds[3]; - for (path = shape->paths->next; path != NULL; path = path->next) { - shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); - shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); - shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); - shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); - } - - // Set fill - if (attr->hasFill == 0) { - shape->fill.type = NSVG_PAINT_NONE; - } else if (attr->hasFill == 1) { - shape->fill.type = NSVG_PAINT_COLOR; - shape->fill.color = attr->fillColor; - shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; - } else if (attr->hasFill == 2) { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, attr->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); - if (shape->fill.gradient == NULL) { - shape->fill.type = NSVG_PAINT_NONE; - } - } - - // Set stroke - if (attr->hasStroke == 0) { - shape->stroke.type = NSVG_PAINT_NONE; - } else if (attr->hasStroke == 1) { - shape->stroke.type = NSVG_PAINT_COLOR; - shape->stroke.color = attr->strokeColor; - shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; - } else if (attr->hasStroke == 2) { - float inv[6], localBounds[4]; - nsvg__xformInverse(inv, attr->xform); - nsvg__getLocalBounds(localBounds, shape, inv); - shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); - if (shape->stroke.gradient == NULL) - shape->stroke.type = NSVG_PAINT_NONE; - } - - // Set flags - shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); - - // Add to tail - if (p->image->shapes == NULL) - p->image->shapes = shape; - else - p->shapesTail->next = shape; - p->shapesTail = shape; - - return; - -error: - if (shape) free(shape); -} - -static void nsvg__addPath(NSVGparser* p, char closed) -{ - NSVGattrib* attr = nsvg__getAttr(p); - NSVGpath* path = NULL; - float bounds[4]; - float* curve; - int i; - - if (p->npts < 4) - return; - - if (closed) - nsvg__lineTo(p, p->pts[0], p->pts[1]); - - path = (NSVGpath*)malloc(sizeof(NSVGpath)); - if (path == NULL) goto error; - memset(path, 0, sizeof(NSVGpath)); - - path->pts = (float*)malloc(p->npts*2*sizeof(float)); - if (path->pts == NULL) goto error; - path->closed = closed; - path->npts = p->npts; - - // Transform path. - for (i = 0; i < p->npts; ++i) - nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); - - // Find bounds - for (i = 0; i < path->npts-1; i += 3) { - curve = &path->pts[i*2]; - nsvg__curveBounds(bounds, curve); - if (i == 0) { - path->bounds[0] = bounds[0]; - path->bounds[1] = bounds[1]; - path->bounds[2] = bounds[2]; - path->bounds[3] = bounds[3]; - } else { - path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); - path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); - path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); - path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); - } - } - - path->next = p->plist; - p->plist = path; - - return; - -error: - if (path != NULL) { - if (path->pts != NULL) free(path->pts); - free(path); - } -} - -// We roll our own string to float because the std library one uses locale and messes things up. -static double nsvg__atof(const char* s) -{ - char* cur = (char*)s; - char* end = NULL; - double res = 0.0, sign = 1.0; - long long intPart = 0, fracPart = 0; - char hasIntPart = 0, hasFracPart = 0; - - // Parse optional sign - if (*cur == '+') { - cur++; - } else if (*cur == '-') { - sign = -1; - cur++; - } - - // Parse integer part - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - intPart = (double)strtoll(cur, &end, 10); - if (cur != end) { - res = (double)intPart; - hasIntPart = 1; - cur = end; - } - } - - // Parse fractional part. - if (*cur == '.') { - cur++; // Skip '.' - if (nsvg__isdigit(*cur)) { - // Parse digit sequence - fracPart = strtoll(cur, &end, 10); - if (cur != end) { - res += (double)fracPart / pow(10.0, (double)(end - cur)); - hasFracPart = 1; - cur = end; - } - } - } - - // A valid number should have integer or fractional part. - if (!hasIntPart && !hasFracPart) - return 0.0; - - // Parse optional exponent - if (*cur == 'e' || *cur == 'E') { - int expPart = 0; - cur++; // skip 'E' - expPart = strtol(cur, &end, 10); // Parse digit sequence with sign - if (cur != end) { - res *= pow(10.0, (double)expPart); - } - } - - return res * sign; -} - - -static const char* nsvg__parseNumber(const char* s, char* it, const int size) -{ - const int last = size-1; - int i = 0; - - // sign - if (*s == '-' || *s == '+') { - if (i < last) it[i++] = *s; - s++; - } - // integer part - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - if (*s == '.') { - // decimal point - if (i < last) it[i++] = *s; - s++; - // fraction part - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - } - // exponent - if (*s == 'e' || *s == 'E') { - if (i < last) it[i++] = *s; - s++; - if (*s == '-' || *s == '+') { - if (i < last) it[i++] = *s; - s++; - } - while (*s && nsvg__isdigit(*s)) { - if (i < last) it[i++] = *s; - s++; - } - } - it[i] = '\0'; - - return s; -} - -static const char* nsvg__getNextPathItem(const char* s, char* it) -{ - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - if (!*s) return s; - if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { - s = nsvg__parseNumber(s, it, 64); - } else { - // Parse command - it[0] = *s++; - it[1] = '\0'; - return s; - } - - return s; -} - -static unsigned int nsvg__parseColorHex(const char* str) -{ - unsigned int c = 0, r = 0, g = 0, b = 0; - int n = 0; - str++; // skip # - // Calculate number of characters. - while(str[n] && !nsvg__isspace(str[n])) - n++; - if (n == 6) { - sscanf(str, "%x", &c); - } else if (n == 3) { - sscanf(str, "%x", &c); - c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8); - c |= c<<4; - } - r = (c >> 16) & 0xff; - g = (c >> 8) & 0xff; - b = c & 0xff; - return NSVG_RGB(r,g,b); -} - -static unsigned int nsvg__parseColorRGB(const char* str) -{ - int r = -1, g = -1, b = -1; - char s1[32]="", s2[32]=""; - sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); - if (strchr(s1, '%')) { - return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100); - } else { - return NSVG_RGB(r,g,b); - } -} - -typedef struct NSVGNamedColor { - const char* name; - unsigned int color; -} NSVGNamedColor; - -NSVGNamedColor nsvg__colors[] = { - - { "red", NSVG_RGB(255, 0, 0) }, - { "green", NSVG_RGB( 0, 128, 0) }, - { "blue", NSVG_RGB( 0, 0, 255) }, - { "yellow", NSVG_RGB(255, 255, 0) }, - { "cyan", NSVG_RGB( 0, 255, 255) }, - { "magenta", NSVG_RGB(255, 0, 255) }, - { "black", NSVG_RGB( 0, 0, 0) }, - { "grey", NSVG_RGB(128, 128, 128) }, - { "gray", NSVG_RGB(128, 128, 128) }, - { "white", NSVG_RGB(255, 255, 255) }, - -#ifdef NANOSVG_ALL_COLOR_KEYWORDS - { "aliceblue", NSVG_RGB(240, 248, 255) }, - { "antiquewhite", NSVG_RGB(250, 235, 215) }, - { "aqua", NSVG_RGB( 0, 255, 255) }, - { "aquamarine", NSVG_RGB(127, 255, 212) }, - { "azure", NSVG_RGB(240, 255, 255) }, - { "beige", NSVG_RGB(245, 245, 220) }, - { "bisque", NSVG_RGB(255, 228, 196) }, - { "blanchedalmond", NSVG_RGB(255, 235, 205) }, - { "blueviolet", NSVG_RGB(138, 43, 226) }, - { "brown", NSVG_RGB(165, 42, 42) }, - { "burlywood", NSVG_RGB(222, 184, 135) }, - { "cadetblue", NSVG_RGB( 95, 158, 160) }, - { "chartreuse", NSVG_RGB(127, 255, 0) }, - { "chocolate", NSVG_RGB(210, 105, 30) }, - { "coral", NSVG_RGB(255, 127, 80) }, - { "cornflowerblue", NSVG_RGB(100, 149, 237) }, - { "cornsilk", NSVG_RGB(255, 248, 220) }, - { "crimson", NSVG_RGB(220, 20, 60) }, - { "darkblue", NSVG_RGB( 0, 0, 139) }, - { "darkcyan", NSVG_RGB( 0, 139, 139) }, - { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, - { "darkgray", NSVG_RGB(169, 169, 169) }, - { "darkgreen", NSVG_RGB( 0, 100, 0) }, - { "darkgrey", NSVG_RGB(169, 169, 169) }, - { "darkkhaki", NSVG_RGB(189, 183, 107) }, - { "darkmagenta", NSVG_RGB(139, 0, 139) }, - { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, - { "darkorange", NSVG_RGB(255, 140, 0) }, - { "darkorchid", NSVG_RGB(153, 50, 204) }, - { "darkred", NSVG_RGB(139, 0, 0) }, - { "darksalmon", NSVG_RGB(233, 150, 122) }, - { "darkseagreen", NSVG_RGB(143, 188, 143) }, - { "darkslateblue", NSVG_RGB( 72, 61, 139) }, - { "darkslategray", NSVG_RGB( 47, 79, 79) }, - { "darkslategrey", NSVG_RGB( 47, 79, 79) }, - { "darkturquoise", NSVG_RGB( 0, 206, 209) }, - { "darkviolet", NSVG_RGB(148, 0, 211) }, - { "deeppink", NSVG_RGB(255, 20, 147) }, - { "deepskyblue", NSVG_RGB( 0, 191, 255) }, - { "dimgray", NSVG_RGB(105, 105, 105) }, - { "dimgrey", NSVG_RGB(105, 105, 105) }, - { "dodgerblue", NSVG_RGB( 30, 144, 255) }, - { "firebrick", NSVG_RGB(178, 34, 34) }, - { "floralwhite", NSVG_RGB(255, 250, 240) }, - { "forestgreen", NSVG_RGB( 34, 139, 34) }, - { "fuchsia", NSVG_RGB(255, 0, 255) }, - { "gainsboro", NSVG_RGB(220, 220, 220) }, - { "ghostwhite", NSVG_RGB(248, 248, 255) }, - { "gold", NSVG_RGB(255, 215, 0) }, - { "goldenrod", NSVG_RGB(218, 165, 32) }, - { "greenyellow", NSVG_RGB(173, 255, 47) }, - { "honeydew", NSVG_RGB(240, 255, 240) }, - { "hotpink", NSVG_RGB(255, 105, 180) }, - { "indianred", NSVG_RGB(205, 92, 92) }, - { "indigo", NSVG_RGB( 75, 0, 130) }, - { "ivory", NSVG_RGB(255, 255, 240) }, - { "khaki", NSVG_RGB(240, 230, 140) }, - { "lavender", NSVG_RGB(230, 230, 250) }, - { "lavenderblush", NSVG_RGB(255, 240, 245) }, - { "lawngreen", NSVG_RGB(124, 252, 0) }, - { "lemonchiffon", NSVG_RGB(255, 250, 205) }, - { "lightblue", NSVG_RGB(173, 216, 230) }, - { "lightcoral", NSVG_RGB(240, 128, 128) }, - { "lightcyan", NSVG_RGB(224, 255, 255) }, - { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, - { "lightgray", NSVG_RGB(211, 211, 211) }, - { "lightgreen", NSVG_RGB(144, 238, 144) }, - { "lightgrey", NSVG_RGB(211, 211, 211) }, - { "lightpink", NSVG_RGB(255, 182, 193) }, - { "lightsalmon", NSVG_RGB(255, 160, 122) }, - { "lightseagreen", NSVG_RGB( 32, 178, 170) }, - { "lightskyblue", NSVG_RGB(135, 206, 250) }, - { "lightslategray", NSVG_RGB(119, 136, 153) }, - { "lightslategrey", NSVG_RGB(119, 136, 153) }, - { "lightsteelblue", NSVG_RGB(176, 196, 222) }, - { "lightyellow", NSVG_RGB(255, 255, 224) }, - { "lime", NSVG_RGB( 0, 255, 0) }, - { "limegreen", NSVG_RGB( 50, 205, 50) }, - { "linen", NSVG_RGB(250, 240, 230) }, - { "maroon", NSVG_RGB(128, 0, 0) }, - { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, - { "mediumblue", NSVG_RGB( 0, 0, 205) }, - { "mediumorchid", NSVG_RGB(186, 85, 211) }, - { "mediumpurple", NSVG_RGB(147, 112, 219) }, - { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, - { "mediumslateblue", NSVG_RGB(123, 104, 238) }, - { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, - { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, - { "mediumvioletred", NSVG_RGB(199, 21, 133) }, - { "midnightblue", NSVG_RGB( 25, 25, 112) }, - { "mintcream", NSVG_RGB(245, 255, 250) }, - { "mistyrose", NSVG_RGB(255, 228, 225) }, - { "moccasin", NSVG_RGB(255, 228, 181) }, - { "navajowhite", NSVG_RGB(255, 222, 173) }, - { "navy", NSVG_RGB( 0, 0, 128) }, - { "oldlace", NSVG_RGB(253, 245, 230) }, - { "olive", NSVG_RGB(128, 128, 0) }, - { "olivedrab", NSVG_RGB(107, 142, 35) }, - { "orange", NSVG_RGB(255, 165, 0) }, - { "orangered", NSVG_RGB(255, 69, 0) }, - { "orchid", NSVG_RGB(218, 112, 214) }, - { "palegoldenrod", NSVG_RGB(238, 232, 170) }, - { "palegreen", NSVG_RGB(152, 251, 152) }, - { "paleturquoise", NSVG_RGB(175, 238, 238) }, - { "palevioletred", NSVG_RGB(219, 112, 147) }, - { "papayawhip", NSVG_RGB(255, 239, 213) }, - { "peachpuff", NSVG_RGB(255, 218, 185) }, - { "peru", NSVG_RGB(205, 133, 63) }, - { "pink", NSVG_RGB(255, 192, 203) }, - { "plum", NSVG_RGB(221, 160, 221) }, - { "powderblue", NSVG_RGB(176, 224, 230) }, - { "purple", NSVG_RGB(128, 0, 128) }, - { "rosybrown", NSVG_RGB(188, 143, 143) }, - { "royalblue", NSVG_RGB( 65, 105, 225) }, - { "saddlebrown", NSVG_RGB(139, 69, 19) }, - { "salmon", NSVG_RGB(250, 128, 114) }, - { "sandybrown", NSVG_RGB(244, 164, 96) }, - { "seagreen", NSVG_RGB( 46, 139, 87) }, - { "seashell", NSVG_RGB(255, 245, 238) }, - { "sienna", NSVG_RGB(160, 82, 45) }, - { "silver", NSVG_RGB(192, 192, 192) }, - { "skyblue", NSVG_RGB(135, 206, 235) }, - { "slateblue", NSVG_RGB(106, 90, 205) }, - { "slategray", NSVG_RGB(112, 128, 144) }, - { "slategrey", NSVG_RGB(112, 128, 144) }, - { "snow", NSVG_RGB(255, 250, 250) }, - { "springgreen", NSVG_RGB( 0, 255, 127) }, - { "steelblue", NSVG_RGB( 70, 130, 180) }, - { "tan", NSVG_RGB(210, 180, 140) }, - { "teal", NSVG_RGB( 0, 128, 128) }, - { "thistle", NSVG_RGB(216, 191, 216) }, - { "tomato", NSVG_RGB(255, 99, 71) }, - { "turquoise", NSVG_RGB( 64, 224, 208) }, - { "violet", NSVG_RGB(238, 130, 238) }, - { "wheat", NSVG_RGB(245, 222, 179) }, - { "whitesmoke", NSVG_RGB(245, 245, 245) }, - { "yellowgreen", NSVG_RGB(154, 205, 50) }, -#endif -}; - -static unsigned int nsvg__parseColorName(const char* str) -{ - int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); - - for (i = 0; i < ncolors; i++) { - if (strcmp(nsvg__colors[i].name, str) == 0) { - return nsvg__colors[i].color; - } - } - - return NSVG_RGB(128, 128, 128); -} - -static unsigned int nsvg__parseColor(const char* str) -{ - size_t len = 0; - while(*str == ' ') ++str; - len = strlen(str); - if (len >= 1 && *str == '#') - return nsvg__parseColorHex(str); - else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') - return nsvg__parseColorRGB(str); - return nsvg__parseColorName(str); -} - -static float nsvg__parseOpacity(const char* str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) val = 0.0f; - if (val > 1.0f) val = 1.0f; - return val; -} - -static float nsvg__parseMiterLimit(const char* str) -{ - float val = nsvg__atof(str); - if (val < 0.0f) val = 0.0f; - return val; -} - -static int nsvg__parseUnits(const char* units) -{ - if (units[0] == 'p' && units[1] == 'x') - return NSVG_UNITS_PX; - else if (units[0] == 'p' && units[1] == 't') - return NSVG_UNITS_PT; - else if (units[0] == 'p' && units[1] == 'c') - return NSVG_UNITS_PC; - else if (units[0] == 'm' && units[1] == 'm') - return NSVG_UNITS_MM; - else if (units[0] == 'c' && units[1] == 'm') - return NSVG_UNITS_CM; - else if (units[0] == 'i' && units[1] == 'n') - return NSVG_UNITS_IN; - else if (units[0] == '%') - return NSVG_UNITS_PERCENT; - else if (units[0] == 'e' && units[1] == 'm') - return NSVG_UNITS_EM; - else if (units[0] == 'e' && units[1] == 'x') - return NSVG_UNITS_EX; - return NSVG_UNITS_USER; -} - -static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) -{ - NSVGcoordinate coord = {0, NSVG_UNITS_USER}; - char buf[64]; - coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); - coord.value = nsvg__atof(buf); - return coord; -} - -static NSVGcoordinate nsvg__coord(float v, int units) -{ - NSVGcoordinate coord = {v, units}; - return coord; -} - -static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) -{ - NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); - return nsvg__convertToPixels(p, coord, orig, length); -} - -static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) -{ - const char* end; - const char* ptr; - char it[64]; - - *na = 0; - ptr = str; - while (*ptr && *ptr != '(') ++ptr; - if (*ptr == 0) - return 1; - end = ptr; - while (*end && *end != ')') ++end; - if (*end == 0) - return 1; - - while (ptr < end) { - if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { - if (*na >= maxNa) return 0; - ptr = nsvg__parseNumber(ptr, it, 64); - args[(*na)++] = (float)nsvg__atof(it); - } else { - ++ptr; - } - } - return (int)(end - str); -} - - -static int nsvg__parseMatrix(float* xform, const char* str) -{ - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, t, 6, &na); - if (na != 6) return len; - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseTranslate(float* xform, const char* str) -{ - float args[2]; - float t[6]; - int na = 0; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) args[1] = 0.0; - - nsvg__xformSetTranslation(t, args[0], args[1]); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseScale(float* xform, const char* str) -{ - float args[2]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 2, &na); - if (na == 1) args[1] = args[0]; - nsvg__xformSetScale(t, args[0], args[1]); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseSkewX(float* xform, const char* str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseSkewY(float* xform, const char* str) -{ - float args[1]; - int na = 0; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 1, &na); - nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); - memcpy(xform, t, sizeof(float)*6); - return len; -} - -static int nsvg__parseRotate(float* xform, const char* str) -{ - float args[3]; - int na = 0; - float m[6]; - float t[6]; - int len = nsvg__parseTransformArgs(str, args, 3, &na); - if (na == 1) - args[1] = args[2] = 0.0f; - nsvg__xformIdentity(m); - - if (na > 1) { - nsvg__xformSetTranslation(t, -args[1], -args[2]); - nsvg__xformMultiply(m, t); - } - - nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); - nsvg__xformMultiply(m, t); - - if (na > 1) { - nsvg__xformSetTranslation(t, args[1], args[2]); - nsvg__xformMultiply(m, t); - } - - memcpy(xform, m, sizeof(float)*6); - - return len; -} - -static void nsvg__parseTransform(float* xform, const char* str) -{ - float t[6]; - nsvg__xformIdentity(xform); - while (*str) - { - if (strncmp(str, "matrix", 6) == 0) - str += nsvg__parseMatrix(t, str); - else if (strncmp(str, "translate", 9) == 0) - str += nsvg__parseTranslate(t, str); - else if (strncmp(str, "scale", 5) == 0) - str += nsvg__parseScale(t, str); - else if (strncmp(str, "rotate", 6) == 0) - str += nsvg__parseRotate(t, str); - else if (strncmp(str, "skewX", 5) == 0) - str += nsvg__parseSkewX(t, str); - else if (strncmp(str, "skewY", 5) == 0) - str += nsvg__parseSkewY(t, str); - else{ - ++str; - continue; - } - - nsvg__xformPremultiply(xform, t); - } -} - -static void nsvg__parseUrl(char* id, const char* str) -{ - int i = 0; - str += 4; // "url("; - if (*str == '#') - str++; - while (i < 63 && *str != ')') { - id[i] = *str++; - i++; - } - id[i] = '\0'; -} - -static char nsvg__parseLineCap(const char* str) -{ - if (strcmp(str, "butt") == 0) - return NSVG_CAP_BUTT; - else if (strcmp(str, "round") == 0) - return NSVG_CAP_ROUND; - else if (strcmp(str, "square") == 0) - return NSVG_CAP_SQUARE; - // TODO: handle inherit. - return NSVG_CAP_BUTT; -} - -static char nsvg__parseLineJoin(const char* str) -{ - if (strcmp(str, "miter") == 0) - return NSVG_JOIN_MITER; - else if (strcmp(str, "round") == 0) - return NSVG_JOIN_ROUND; - else if (strcmp(str, "bevel") == 0) - return NSVG_JOIN_BEVEL; - // TODO: handle inherit. - return NSVG_JOIN_MITER; -} - -static char nsvg__parseFillRule(const char* str) -{ - if (strcmp(str, "nonzero") == 0) - return NSVG_FILLRULE_NONZERO; - else if (strcmp(str, "evenodd") == 0) - return NSVG_FILLRULE_EVENODD; - // TODO: handle inherit. - return NSVG_FILLRULE_NONZERO; -} - -static const char* nsvg__getNextDashItem(const char* s, char* it) -{ - int n = 0; - it[0] = '\0'; - // Skip white spaces and commas - while (*s && (nsvg__isspace(*s) || *s == ',')) s++; - // Advance until whitespace, comma or end. - while (*s && (!nsvg__isspace(*s) && *s != ',')) { - if (n < 63) - it[n++] = *s; - s++; - } - it[n++] = '\0'; - return s; -} - -static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) -{ - char item[64]; - int count = 0, i; - float sum = 0.0f; - - // Handle "none" - if (str[0] == 'n') - return 0; - - // Parse dashes - while (*str) { - str = nsvg__getNextDashItem(str, item); - if (!*item) break; - if (count < NSVG_MAX_DASHES) - strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); - } - - for (i = 0; i < count; i++) - sum += strokeDashArray[i]; - if (sum <= 1e-6f) - count = 0; - - return count; -} - -static void nsvg__parseStyle(NSVGparser* p, const char* str); - -static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) -{ - float xform[6]; - NSVGattrib* attr = nsvg__getAttr(p); - if (!attr) return 0; - - if (strcmp(name, "style") == 0) { - nsvg__parseStyle(p, value); - } else if (strcmp(name, "display") == 0) { - if (strcmp(value, "none") == 0) - attr->visible = 0; - // Don't reset ->visible on display:inline, one display:none hides the whole subtree - - } else if (strcmp(name, "fill") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasFill = 0; - } else if (strncmp(value, "url(", 4) == 0) { - attr->hasFill = 2; - nsvg__parseUrl(attr->fillGradient, value); - } else { - attr->hasFill = 1; - attr->fillColor = nsvg__parseColor(value); - } - } else if (strcmp(name, "opacity") == 0) { - attr->opacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "fill-opacity") == 0) { - attr->fillOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "stroke") == 0) { - if (strcmp(value, "none") == 0) { - attr->hasStroke = 0; - } else if (strncmp(value, "url(", 4) == 0) { - attr->hasStroke = 2; - nsvg__parseUrl(attr->strokeGradient, value); - } else { - attr->hasStroke = 1; - attr->strokeColor = nsvg__parseColor(value); - } - } else if (strcmp(name, "stroke-width") == 0) { - attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "stroke-dasharray") == 0) { - attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); - } else if (strcmp(name, "stroke-dashoffset") == 0) { - attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "stroke-opacity") == 0) { - attr->strokeOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "stroke-linecap") == 0) { - attr->strokeLineCap = nsvg__parseLineCap(value); - } else if (strcmp(name, "stroke-linejoin") == 0) { - attr->strokeLineJoin = nsvg__parseLineJoin(value); - } else if (strcmp(name, "stroke-miterlimit") == 0) { - attr->miterLimit = nsvg__parseMiterLimit(value); - } else if (strcmp(name, "fill-rule") == 0) { - attr->fillRule = nsvg__parseFillRule(value); - } else if (strcmp(name, "font-size") == 0) { - attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); - } else if (strcmp(name, "transform") == 0) { - nsvg__parseTransform(xform, value); - nsvg__xformPremultiply(attr->xform, xform); - } else if (strcmp(name, "stop-color") == 0) { - attr->stopColor = nsvg__parseColor(value); - } else if (strcmp(name, "stop-opacity") == 0) { - attr->stopOpacity = nsvg__parseOpacity(value); - } else if (strcmp(name, "offset") == 0) { - attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); - } else if (strcmp(name, "id") == 0) { - strncpy(attr->id, value, 63); - attr->id[63] = '\0'; - } else { - return 0; - } - return 1; -} - -static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) -{ - const char* str; - const char* val; - char name[512]; - char value[512]; - int n; - - str = start; - while (str < end && *str != ':') ++str; - - val = str; - - // Right Trim - while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; - ++str; - - n = (int)(str - start); - if (n > 511) n = 511; - if (n) memcpy(name, start, n); - name[n] = 0; - - while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; - - n = (int)(end - val); - if (n > 511) n = 511; - if (n) memcpy(value, val, n); - value[n] = 0; - - return nsvg__parseAttr(p, name, value); -} - -static void nsvg__parseStyle(NSVGparser* p, const char* str) -{ - const char* start; - const char* end; - - while (*str) { - // Left Trim - while(*str && nsvg__isspace(*str)) ++str; - start = str; - while(*str && *str != ';') ++str; - end = str; - - // Right Trim - while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; - ++end; - - nsvg__parseNameValue(p, start, end); - if (*str) ++str; - } -} - -static void nsvg__parseAttribs(NSVGparser* p, const char** attr) -{ - int i; - for (i = 0; attr[i]; i += 2) - { - if (strcmp(attr[i], "style") == 0) - nsvg__parseStyle(p, attr[i + 1]); - else - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } -} - -static int nsvg__getArgsPerElement(char cmd) -{ - switch (cmd) { - case 'v': - case 'V': - case 'h': - case 'H': - return 1; - case 'm': - case 'M': - case 'l': - case 'L': - case 't': - case 'T': - return 2; - case 'q': - case 'Q': - case 's': - case 'S': - return 4; - case 'c': - case 'C': - return 6; - case 'a': - case 'A': - return 7; - } - return 0; -} - -static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__moveTo(p, *cpx, *cpy); -} - -static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) { - *cpx += args[0]; - *cpy += args[1]; - } else { - *cpx = args[0]; - *cpy = args[1]; - } - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) - *cpx += args[0]; - else - *cpx = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - if (rel) - *cpy += args[0]; - else - *cpy = args[0]; - nsvg__lineTo(p, *cpx, *cpy); -} - -static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x2, y2, cx1, cy1, cx2, cy2; - - if (rel) { - cx1 = *cpx + args[0]; - cy1 = *cpy + args[1]; - cx2 = *cpx + args[2]; - cy2 = *cpy + args[3]; - x2 = *cpx + args[4]; - y2 = *cpy + args[5]; - } else { - cx1 = args[0]; - cy1 = args[1]; - cx2 = args[2]; - cy2 = args[3]; - x2 = args[4]; - y2 = args[5]; - } - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx2 = *cpx + args[0]; - cy2 = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } else { - cx2 = args[0]; - cy2 = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - cx1 = 2*x1 - *cpx2; - cy1 = 2*y1 - *cpy2; - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx2; - *cpy2 = cy2; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - cx = *cpx + args[0]; - cy = *cpy + args[1]; - x2 = *cpx + args[2]; - y2 = *cpy + args[3]; - } else { - cx = args[0]; - cy = args[1]; - x2 = args[2]; - y2 = args[3]; - } - - // Convert to cubic bezier - cx1 = x1 + 2.0f/3.0f*(cx - x1); - cy1 = y1 + 2.0f/3.0f*(cy - y1); - cx2 = x2 + 2.0f/3.0f*(cx - x2); - cy2 = y2 + 2.0f/3.0f*(cy - y2); - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, - float* cpx2, float* cpy2, float* args, int rel) -{ - float x1, y1, x2, y2, cx, cy; - float cx1, cy1, cx2, cy2; - - x1 = *cpx; - y1 = *cpy; - if (rel) { - x2 = *cpx + args[0]; - y2 = *cpy + args[1]; - } else { - x2 = args[0]; - y2 = args[1]; - } - - cx = 2*x1 - *cpx2; - cy = 2*y1 - *cpy2; - - // Convert to cubix bezier - cx1 = x1 + 2.0f/3.0f*(cx - x1); - cy1 = y1 + 2.0f/3.0f*(cy - y1); - cx2 = x2 + 2.0f/3.0f*(cx - x2); - cy2 = y2 + 2.0f/3.0f*(cy - y2); - - nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); - - *cpx2 = cx; - *cpy2 = cy; - *cpx = x2; - *cpy = y2; -} - -static float nsvg__sqr(float x) { return x*x; } -static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } - -static float nsvg__vecrat(float ux, float uy, float vx, float vy) -{ - return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); -} - -static float nsvg__vecang(float ux, float uy, float vx, float vy) -{ - float r = nsvg__vecrat(ux,uy, vx,vy); - if (r < -1.0f) r = -1.0f; - if (r > 1.0f) r = 1.0f; - return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); -} - -static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) -{ - // Ported from canvg (https://code.google.com/p/canvg/) - float rx, ry, rotx; - float x1, y1, x2, y2, cx, cy, dx, dy, d; - float x1p, y1p, cxp, cyp, s, sa, sb; - float ux, uy, vx, vy, a1, da; - float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; - float sinrx, cosrx; - int fa, fs; - int i, ndivs; - float hda, kappa; - - rx = fabsf(args[0]); // y radius - ry = fabsf(args[1]); // x radius - rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle - fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc - fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction - x1 = *cpx; // start point - y1 = *cpy; - if (rel) { // end point - x2 = *cpx + args[5]; - y2 = *cpy + args[6]; - } else { - x2 = args[5]; - y2 = args[6]; - } - - dx = x1 - x2; - dy = y1 - y2; - d = sqrtf(dx*dx + dy*dy); - if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { - // The arc degenerates to a line - nsvg__lineTo(p, x2, y2); - *cpx = x2; - *cpy = y2; - return; - } - - sinrx = sinf(rotx); - cosrx = cosf(rotx); - - // Convert to center point parameterization. - // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes - // 1) Compute x1', y1' - x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; - y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; - d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); - if (d > 1) { - d = sqrtf(d); - rx *= d; - ry *= d; - } - // 2) Compute cx', cy' - s = 0.0f; - sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); - sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); - if (sa < 0.0f) sa = 0.0f; - if (sb > 0.0f) - s = sqrtf(sa / sb); - if (fa == fs) - s = -s; - cxp = s * rx * y1p / ry; - cyp = s * -ry * x1p / rx; - - // 3) Compute cx,cy from cx',cy' - cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; - cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; - - // 4) Calculate theta1, and delta theta. - ux = (x1p - cxp) / rx; - uy = (y1p - cyp) / ry; - vx = (-x1p - cxp) / rx; - vy = (-y1p - cyp) / ry; - a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle - da = nsvg__vecang(ux,uy, vx,vy); // Delta angle - -// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; -// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; - - if (fs == 0 && da > 0) - da -= 2 * NSVG_PI; - else if (fs == 1 && da < 0) - da += 2 * NSVG_PI; - - // Approximate the arc using cubic spline segments. - t[0] = cosrx; t[1] = sinrx; - t[2] = -sinrx; t[3] = cosrx; - t[4] = cx; t[5] = cy; - - // Split arc into max 90 degree segments. - // The loop assumes an iteration per end point (including start and end), this +1. - ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); - hda = (da / (float)ndivs) / 2.0f; - kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda)); - if (da < 0.0f) - kappa = -kappa; - - for (i = 0; i <= ndivs; i++) { - a = a1 + da * ((float)i/(float)ndivs); - dx = cosf(a); - dy = sinf(a); - nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position - nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent - if (i > 0) - nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); - px = x; - py = y; - ptanx = tanx; - ptany = tany; - } - - *cpx = x2; - *cpy = y2; -} - -static void nsvg__parsePath(NSVGparser* p, const char** attr) -{ - const char* s = NULL; - char cmd = '\0'; - float args[10]; - int nargs; - int rargs = 0; - float cpx, cpy, cpx2, cpy2; - const char* tmp[4]; - char closedFlag; - int i; - char item[64]; - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "d") == 0) { - s = attr[i + 1]; - } else { - tmp[0] = attr[i]; - tmp[1] = attr[i + 1]; - tmp[2] = 0; - tmp[3] = 0; - nsvg__parseAttribs(p, tmp); - } - } - - if (s) { - nsvg__resetPath(p); - cpx = 0; cpy = 0; - cpx2 = 0; cpy2 = 0; - closedFlag = 0; - nargs = 0; - - while (*s) { - s = nsvg__getNextPathItem(s, item); - if (!*item) break; - if (nsvg__isnum(item[0])) { - if (nargs < 10) - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= rargs) { - switch (cmd) { - case 'm': - case 'M': - nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); - // Moveto can be followed by multiple coordinate pairs, - // which should be treated as linetos. - cmd = (cmd == 'm') ? 'l' : 'L'; - rargs = nsvg__getArgsPerElement(cmd); - cpx2 = cpx; cpy2 = cpy; - break; - case 'l': - case 'L': - nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - case 'H': - case 'h': - nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - case 'V': - case 'v': - nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - case 'C': - case 'c': - nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); - break; - case 'S': - case 's': - nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); - break; - case 'Q': - case 'q': - nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); - break; - case 'T': - case 't': - nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); - break; - case 'A': - case 'a': - nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; - break; - default: - if (nargs >= 2) { - cpx = args[nargs-2]; - cpy = args[nargs-1]; - cpx2 = cpx; cpy2 = cpy; - } - break; - } - nargs = 0; - } - } else { - cmd = item[0]; - rargs = nsvg__getArgsPerElement(cmd); - if (cmd == 'M' || cmd == 'm') { - // Commit path. - if (p->npts > 0) - nsvg__addPath(p, closedFlag); - // Start new subpath. - nsvg__resetPath(p); - closedFlag = 0; - nargs = 0; - } else if (cmd == 'Z' || cmd == 'z') { - closedFlag = 1; - // Commit path. - if (p->npts > 0) { - // Move current point to first point - cpx = p->pts[0]; - cpy = p->pts[1]; - cpx2 = cpx; cpy2 = cpy; - nsvg__addPath(p, closedFlag); - } - // Start new subpath. - nsvg__resetPath(p); - nsvg__moveTo(p, cpx, cpy); - closedFlag = 0; - nargs = 0; - } - } - } - // Commit path. - if (p->npts) - nsvg__addPath(p, closedFlag); - } - - nsvg__addShape(p); -} - -static void nsvg__parseRect(NSVGparser* p, const char** attr) -{ - float x = 0.0f; - float y = 0.0f; - float w = 0.0f; - float h = 0.0f; - float rx = -1.0f; // marks not set - float ry = -1.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); - if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx < 0.0f && ry > 0.0f) rx = ry; - if (ry < 0.0f && rx > 0.0f) ry = rx; - if (rx < 0.0f) rx = 0.0f; - if (ry < 0.0f) ry = 0.0f; - if (rx > w/2.0f) rx = w/2.0f; - if (ry > h/2.0f) ry = h/2.0f; - - if (w != 0.0f && h != 0.0f) { - nsvg__resetPath(p); - - if (rx < 0.00001f || ry < 0.0001f) { - nsvg__moveTo(p, x, y); - nsvg__lineTo(p, x+w, y); - nsvg__lineTo(p, x+w, y+h); - nsvg__lineTo(p, x, y+h); - } else { - // Rounded rectangle - nsvg__moveTo(p, x+rx, y); - nsvg__lineTo(p, x+w-rx, y); - nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); - nsvg__lineTo(p, x+w, y+h-ry); - nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); - nsvg__lineTo(p, x+rx, y+h); - nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); - nsvg__lineTo(p, x, y+ry); - nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); - } - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseCircle(NSVGparser* p, const char** attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float r = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); - } - } - - if (r > 0.0f) { - nsvg__resetPath(p); - - nsvg__moveTo(p, cx+r, cy); - nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); - nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); - nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); - nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseEllipse(NSVGparser* p, const char** attr) -{ - float cx = 0.0f; - float cy = 0.0f; - float rx = 0.0f; - float ry = 0.0f; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); - if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); - } - } - - if (rx > 0.0f && ry > 0.0f) { - - nsvg__resetPath(p); - - nsvg__moveTo(p, cx+rx, cy); - nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); - nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); - nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); - nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); - - nsvg__addPath(p, 1); - - nsvg__addShape(p); - } -} - -static void nsvg__parseLine(NSVGparser* p, const char** attr) -{ - float x1 = 0.0; - float y1 = 0.0; - float x2 = 0.0; - float y2 = 0.0; - int i; - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); - if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); - } - } - - nsvg__resetPath(p); - - nsvg__moveTo(p, x1, y1); - nsvg__lineTo(p, x2, y2); - - nsvg__addPath(p, 0); - - nsvg__addShape(p); -} - -static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) -{ - int i; - const char* s; - float args[2]; - int nargs, npts = 0; - char item[64]; - - nsvg__resetPath(p); - - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "points") == 0) { - s = attr[i + 1]; - nargs = 0; - while (*s) { - s = nsvg__getNextPathItem(s, item); - args[nargs++] = (float)nsvg__atof(item); - if (nargs >= 2) { - if (npts == 0) - nsvg__moveTo(p, args[0], args[1]); - else - nsvg__lineTo(p, args[0], args[1]); - nargs = 0; - npts++; - } - } - } - } - } - - nsvg__addPath(p, (char)closeFlag); - - nsvg__addShape(p); -} - -static void nsvg__parseSVG(NSVGparser* p, const char** attr) -{ - int i; - for (i = 0; attr[i]; i += 2) { - if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "width") == 0) { - p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } else if (strcmp(attr[i], "height") == 0) { - p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); - } else if (strcmp(attr[i], "viewBox") == 0) { - const char *s = attr[i + 1]; - char buf[64]; - s = nsvg__parseNumber(s, buf, 64); - p->viewMinx = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewMiny = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewWidth = nsvg__atof(buf); - while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; - if (!*s) return; - s = nsvg__parseNumber(s, buf, 64); - p->viewHeight = nsvg__atof(buf); - } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { - if (strstr(attr[i + 1], "none") != 0) { - // No uniform scaling - p->alignType = NSVG_ALIGN_NONE; - } else { - // Parse X align - if (strstr(attr[i + 1], "xMin") != 0) - p->alignX = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "xMid") != 0) - p->alignX = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "xMax") != 0) - p->alignX = NSVG_ALIGN_MAX; - // Parse X align - if (strstr(attr[i + 1], "yMin") != 0) - p->alignY = NSVG_ALIGN_MIN; - else if (strstr(attr[i + 1], "yMid") != 0) - p->alignY = NSVG_ALIGN_MID; - else if (strstr(attr[i + 1], "yMax") != 0) - p->alignY = NSVG_ALIGN_MAX; - // Parse meet/slice - p->alignType = NSVG_ALIGN_MEET; - if (strstr(attr[i + 1], "slice") != 0) - p->alignType = NSVG_ALIGN_SLICE; - } - } - } - } -} - -static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) -{ - int i; - NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); - if (grad == NULL) return; - memset(grad, 0, sizeof(NSVGgradientData)); - grad->units = NSVG_OBJECT_SPACE; - grad->type = type; - if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { - grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); - grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); - } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { - grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); - } - - nsvg__xformIdentity(grad->xform); - - for (i = 0; attr[i]; i += 2) { - if (strcmp(attr[i], "id") == 0) { - strncpy(grad->id, attr[i+1], 63); - grad->id[63] = '\0'; - } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { - if (strcmp(attr[i], "gradientUnits") == 0) { - if (strcmp(attr[i+1], "objectBoundingBox") == 0) - grad->units = NSVG_OBJECT_SPACE; - else - grad->units = NSVG_USER_SPACE; - } else if (strcmp(attr[i], "gradientTransform") == 0) { - nsvg__parseTransform(grad->xform, attr[i + 1]); - } else if (strcmp(attr[i], "cx") == 0) { - grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "cy") == 0) { - grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "r") == 0) { - grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "fx") == 0) { - grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "fy") == 0) { - grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "x1") == 0) { - grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "y1") == 0) { - grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "x2") == 0) { - grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "y2") == 0) { - grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); - } else if (strcmp(attr[i], "spreadMethod") == 0) { - if (strcmp(attr[i+1], "pad") == 0) - grad->spread = NSVG_SPREAD_PAD; - else if (strcmp(attr[i+1], "reflect") == 0) - grad->spread = NSVG_SPREAD_REFLECT; - else if (strcmp(attr[i+1], "repeat") == 0) - grad->spread = NSVG_SPREAD_REPEAT; - } else if (strcmp(attr[i], "xlink:href") == 0) { - const char *href = attr[i+1]; - strncpy(grad->ref, href+1, 62); - grad->ref[62] = '\0'; - } - } - } - - grad->next = p->gradients; - p->gradients = grad; -} - -static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) -{ - NSVGattrib* curAttr = nsvg__getAttr(p); - NSVGgradientData* grad; - NSVGgradientStop* stop; - int i, idx; - - curAttr->stopOffset = 0; - curAttr->stopColor = 0; - curAttr->stopOpacity = 1.0f; - - for (i = 0; attr[i]; i += 2) { - nsvg__parseAttr(p, attr[i], attr[i + 1]); - } - - // Add stop to the last gradient. - grad = p->gradients; - if (grad == NULL) return; - - grad->nstops++; - grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); - if (grad->stops == NULL) return; - - // Insert - idx = grad->nstops-1; - for (i = 0; i < grad->nstops-1; i++) { - if (curAttr->stopOffset < grad->stops[i].offset) { - idx = i; - break; - } - } - if (idx != grad->nstops-1) { - for (i = grad->nstops-1; i > idx; i--) - grad->stops[i] = grad->stops[i-1]; - } - - stop = &grad->stops[idx]; - stop->color = curAttr->stopColor; - stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; - stop->offset = curAttr->stopOffset; -} - -static void nsvg__startElement(void* ud, const char* el, const char** attr) -{ - NSVGparser* p = (NSVGparser*)ud; - - if (p->defsFlag) { - // Skip everything but gradients in defs - if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } - return; - } - - if (strcmp(el, "g") == 0) { - nsvg__pushAttr(p); - nsvg__parseAttribs(p, attr); - } else if (strcmp(el, "path") == 0) { - if (p->pathFlag) // Do not allow nested paths. - return; - nsvg__pushAttr(p); - nsvg__parsePath(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "rect") == 0) { - nsvg__pushAttr(p); - nsvg__parseRect(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "circle") == 0) { - nsvg__pushAttr(p); - nsvg__parseCircle(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "ellipse") == 0) { - nsvg__pushAttr(p); - nsvg__parseEllipse(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "line") == 0) { - nsvg__pushAttr(p); - nsvg__parseLine(p, attr); - nsvg__popAttr(p); - } else if (strcmp(el, "polyline") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 0); - nsvg__popAttr(p); - } else if (strcmp(el, "polygon") == 0) { - nsvg__pushAttr(p); - nsvg__parsePoly(p, attr, 1); - nsvg__popAttr(p); - } else if (strcmp(el, "linearGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); - } else if (strcmp(el, "radialGradient") == 0) { - nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); - } else if (strcmp(el, "stop") == 0) { - nsvg__parseGradientStop(p, attr); - } else if (strcmp(el, "defs") == 0) { - p->defsFlag = 1; - } else if (strcmp(el, "svg") == 0) { - nsvg__parseSVG(p, attr); - } -} - -static void nsvg__endElement(void* ud, const char* el) -{ - NSVGparser* p = (NSVGparser*)ud; - - if (strcmp(el, "g") == 0) { - nsvg__popAttr(p); - } else if (strcmp(el, "path") == 0) { - p->pathFlag = 0; - } else if (strcmp(el, "defs") == 0) { - p->defsFlag = 0; - } -} - -static void nsvg__content(void* ud, const char* s) -{ - NSVG_NOTUSED(ud); - NSVG_NOTUSED(s); - // empty -} - -static void nsvg__imageBounds(NSVGparser* p, float* bounds) -{ - NSVGshape* shape; - shape = p->image->shapes; - if (shape == NULL) { - bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; - return; - } - bounds[0] = shape->bounds[0]; - bounds[1] = shape->bounds[1]; - bounds[2] = shape->bounds[2]; - bounds[3] = shape->bounds[3]; - for (shape = shape->next; shape != NULL; shape = shape->next) { - bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); - bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); - bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); - bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); - } -} - -static float nsvg__viewAlign(float content, float container, int type) -{ - if (type == NSVG_ALIGN_MIN) - return 0; - else if (type == NSVG_ALIGN_MAX) - return container - content; - // mid - return (container - content) * 0.5f; -} - -static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) -{ - float t[6]; - nsvg__xformSetTranslation(t, tx, ty); - nsvg__xformMultiply (grad->xform, t); - - nsvg__xformSetScale(t, sx, sy); - nsvg__xformMultiply (grad->xform, t); -} - -static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) -{ - NSVGshape* shape; - NSVGpath* path; - float tx, ty, sx, sy, us, bounds[4], t[6], avgs; - int i; - float* pt; - - // Guess image size if not set completely. - nsvg__imageBounds(p, bounds); - - if (p->viewWidth == 0) { - if (p->image->width > 0) { - p->viewWidth = p->image->width; - } else { - p->viewMinx = bounds[0]; - p->viewWidth = bounds[2] - bounds[0]; - } - } - if (p->viewHeight == 0) { - if (p->image->height > 0) { - p->viewHeight = p->image->height; - } else { - p->viewMiny = bounds[1]; - p->viewHeight = bounds[3] - bounds[1]; - } - } - if (p->image->width == 0) - p->image->width = p->viewWidth; - if (p->image->height == 0) - p->image->height = p->viewHeight; - - tx = -p->viewMinx; - ty = -p->viewMiny; - sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; - sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; - // Unit scaling - us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); - - // Fix aspect ratio - if (p->alignType == NSVG_ALIGN_MEET) { - // fit whole image into viewbox - sx = sy = nsvg__minf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; - } else if (p->alignType == NSVG_ALIGN_SLICE) { - // fill whole viewbox with image - sx = sy = nsvg__maxf(sx, sy); - tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; - ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; - } - - // Transform - sx *= us; - sy *= us; - avgs = (sx+sy) / 2.0f; - for (shape = p->image->shapes; shape != NULL; shape = shape->next) { - shape->bounds[0] = (shape->bounds[0] + tx) * sx; - shape->bounds[1] = (shape->bounds[1] + ty) * sy; - shape->bounds[2] = (shape->bounds[2] + tx) * sx; - shape->bounds[3] = (shape->bounds[3] + ty) * sy; - for (path = shape->paths; path != NULL; path = path->next) { - path->bounds[0] = (path->bounds[0] + tx) * sx; - path->bounds[1] = (path->bounds[1] + ty) * sy; - path->bounds[2] = (path->bounds[2] + tx) * sx; - path->bounds[3] = (path->bounds[3] + ty) * sy; - for (i =0; i < path->npts; i++) { - pt = &path->pts[i*2]; - pt[0] = (pt[0] + tx) * sx; - pt[1] = (pt[1] + ty) * sy; - } - } - - if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); - memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); - nsvg__xformInverse(shape->fill.gradient->xform, t); - } - if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { - nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); - memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); - nsvg__xformInverse(shape->stroke.gradient->xform, t); - } - - shape->strokeWidth *= avgs; - shape->strokeDashOffset *= avgs; - for (i = 0; i < shape->strokeDashCount; i++) - shape->strokeDashArray[i] *= avgs; - } -} - -NSVGimage* nsvgParse(char* input, const char* units, float dpi) -{ - NSVGparser* p; - NSVGimage* ret = 0; - - p = nsvg__createParser(); - if (p == NULL) { - return NULL; - } - p->dpi = dpi; - - nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); - - // Scale to viewBox - nsvg__scaleToViewbox(p, units); - - ret = p->image; - p->image = NULL; - - nsvg__deleteParser(p); - - return ret; -} - -#include - -NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) -{ - FILE* fp = NULL; - size_t size; - char* data = NULL; - NSVGimage* image = NULL; - - fp = boost::nowide::fopen(filename, "rb"); - if (!fp) goto error; - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - data = (char*)malloc(size+1); - if (data == NULL) goto error; - if (fread(data, 1, size, fp) != size) goto error; - data[size] = '\0'; // Must be null terminated. - fclose(fp); - - image = nsvgParse(data, units, dpi); - free(data); - return image; - -error: - if (fp) fclose(fp); - if (data) free(data); - if (image) nsvgDelete(image); - return NULL; -} - -NSVGpath* nsvgDuplicatePath(NSVGpath* p) -{ - NSVGpath* res = NULL; - - if (p == NULL) - return NULL; - - res = (NSVGpath*)malloc(sizeof(NSVGpath)); - if (res == NULL) goto error; - memset(res, 0, sizeof(NSVGpath)); - - res->pts = (float*)malloc(p->npts*2*sizeof(float)); - if (res->pts == NULL) goto error; - memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); - res->npts = p->npts; - - memcpy(res->bounds, p->bounds, sizeof(p->bounds)); - - res->closed = p->closed; - - return res; - -error: - if (res != NULL) { - free(res->pts); - free(res); - } - return NULL; -} - -void nsvgDelete(NSVGimage* image) -{ - NSVGshape *snext, *shape; - if (image == NULL) return; - shape = image->shapes; - while (shape != NULL) { - snext = shape->next; - nsvg__deletePaths(shape->paths); - nsvg__deletePaint(&shape->fill); - nsvg__deletePaint(&shape->stroke); - free(shape); - shape = snext; - } - free(image); -} - -#endif diff --git a/src/nanosvg/nanosvgrast.h b/src/nanosvg/nanosvgrast.h deleted file mode 100644 index b740c316c..000000000 --- a/src/nanosvg/nanosvgrast.h +++ /dev/null @@ -1,1452 +0,0 @@ -/* - * Copyright (c) 2013-14 Mikko Mononen memon@inside.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * The polygon rasterization is heavily based on stb_truetype rasterizer - * by Sean Barrett - http://nothings.org/ - * - */ - -#ifndef NANOSVGRAST_H -#define NANOSVGRAST_H - -#ifndef NANOSVGRAST_CPLUSPLUS -#ifdef __cplusplus -extern "C" { -#endif -#endif - -typedef struct NSVGrasterizer NSVGrasterizer; - -/* Example Usage: - // Load SVG - NSVGimage* image; - image = nsvgParseFromFile("test.svg", "px", 96); - - // Create rasterizer (can be used to render multiple images). - struct NSVGrasterizer* rast = nsvgCreateRasterizer(); - // Allocate memory for image - unsigned char* img = malloc(w*h*4); - // Rasterize - nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); -*/ - -// Allocated rasterizer context. -NSVGrasterizer* nsvgCreateRasterizer(); - -// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) -// r - pointer to rasterizer context -// image - pointer to image to rasterize -// tx,ty - image offset (applied after scaling) -// scale - image scale -// dst - pointer to destination image data, 4 bytes per pixel (RGBA) -// w - width of the image to render -// h - height of the image to render -// stride - number of bytes per scaleline in the destination buffer -void nsvgRasterize(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride); - -// Deletes rasterizer context. -void nsvgDeleteRasterizer(NSVGrasterizer*); - - -#ifndef NANOSVGRAST_CPLUSPLUS -#ifdef __cplusplus -} -#endif -#endif - -#endif // NANOSVGRAST_H - -#ifdef NANOSVGRAST_IMPLEMENTATION - -#include - -#define NSVG__SUBSAMPLES 5 -#define NSVG__FIXSHIFT 10 -#define NSVG__FIX (1 << NSVG__FIXSHIFT) -#define NSVG__FIXMASK (NSVG__FIX-1) -#define NSVG__MEMPAGE_SIZE 1024 - -typedef struct NSVGedge { - float x0,y0, x1,y1; - int dir; - struct NSVGedge* next; -} NSVGedge; - -typedef struct NSVGpoint { - float x, y; - float dx, dy; - float len; - float dmx, dmy; - unsigned char flags; -} NSVGpoint; - -typedef struct NSVGactiveEdge { - int x,dx; - float ey; - int dir; - struct NSVGactiveEdge *next; -} NSVGactiveEdge; - -typedef struct NSVGmemPage { - unsigned char mem[NSVG__MEMPAGE_SIZE]; - int size; - struct NSVGmemPage* next; -} NSVGmemPage; - -typedef struct NSVGcachedPaint { - char type; - char spread; - float xform[6]; - unsigned int colors[256]; -} NSVGcachedPaint; - -struct NSVGrasterizer -{ - float px, py; - - float tessTol; - float distTol; - - NSVGedge* edges; - int nedges; - int cedges; - - NSVGpoint* points; - int npoints; - int cpoints; - - NSVGpoint* points2; - int npoints2; - int cpoints2; - - NSVGactiveEdge* freelist; - NSVGmemPage* pages; - NSVGmemPage* curpage; - - unsigned char* scanline; - int cscanline; - - unsigned char* bitmap; - int width, height, stride; -}; - -NSVGrasterizer* nsvgCreateRasterizer() -{ - NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); - if (r == NULL) goto error; - memset(r, 0, sizeof(NSVGrasterizer)); - - r->tessTol = 0.25f; - r->distTol = 0.01f; - - return r; - -error: - nsvgDeleteRasterizer(r); - return NULL; -} - -void nsvgDeleteRasterizer(NSVGrasterizer* r) -{ - NSVGmemPage* p; - - if (r == NULL) return; - - p = r->pages; - while (p != NULL) { - NSVGmemPage* next = p->next; - free(p); - p = next; - } - - if (r->edges) free(r->edges); - if (r->points) free(r->points); - if (r->points2) free(r->points2); - if (r->scanline) free(r->scanline); - - free(r); -} - -static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) -{ - NSVGmemPage *newp; - - // If using existing chain, return the next page in chain - if (cur != NULL && cur->next != NULL) { - return cur->next; - } - - // Alloc new page - newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); - if (newp == NULL) return NULL; - memset(newp, 0, sizeof(NSVGmemPage)); - - // Add to linked list - if (cur != NULL) - cur->next = newp; - else - r->pages = newp; - - return newp; -} - -static void nsvg__resetPool(NSVGrasterizer* r) -{ - NSVGmemPage* p = r->pages; - while (p != NULL) { - p->size = 0; - p = p->next; - } - r->curpage = r->pages; -} - -static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) -{ - unsigned char* buf; - if (size > NSVG__MEMPAGE_SIZE) return NULL; - if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { - r->curpage = nsvg__nextPage(r, r->curpage); - } - buf = &r->curpage->mem[r->curpage->size]; - r->curpage->size += size; - return buf; -} - -static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) -{ - float dx = x2 - x1; - float dy = y2 - y1; - return dx*dx + dy*dy < tol*tol; -} - -static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) -{ - NSVGpoint* pt; - - if (r->npoints > 0) { - pt = &r->points[r->npoints-1]; - if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { - pt->flags = (unsigned char)(pt->flags | flags); - return; - } - } - - if (r->npoints+1 > r->cpoints) { - r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; - r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); - if (r->points == NULL) return; - } - - pt = &r->points[r->npoints]; - pt->x = x; - pt->y = y; - pt->flags = (unsigned char)flags; - r->npoints++; -} - -static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) -{ - if (r->npoints+1 > r->cpoints) { - r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; - r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); - if (r->points == NULL) return; - } - r->points[r->npoints] = pt; - r->npoints++; -} - -static void nsvg__duplicatePoints(NSVGrasterizer* r) -{ - if (r->npoints > r->cpoints2) { - r->cpoints2 = r->npoints; - r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); - if (r->points2 == NULL) return; - } - - memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); - r->npoints2 = r->npoints; -} - -static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) -{ - NSVGedge* e; - - // Skip horizontal edges - if (y0 == y1) - return; - - if (r->nedges+1 > r->cedges) { - r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; - r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); - if (r->edges == NULL) return; - } - - e = &r->edges[r->nedges]; - r->nedges++; - - if (y0 < y1) { - e->x0 = x0; - e->y0 = y0; - e->x1 = x1; - e->y1 = y1; - e->dir = 1; - } else { - e->x0 = x1; - e->y0 = y1; - e->x1 = x0; - e->y1 = y0; - e->dir = -1; - } -} - -static float nsvg__normalize(float *x, float* y) -{ - float d = sqrtf((*x)*(*x) + (*y)*(*y)); - if (d > 1e-6f) { - float id = 1.0f / d; - *x *= id; - *y *= id; - } - return d; -} - -static float nsvg__absf(float x) { return x < 0 ? -x : x; } - -static void nsvg__flattenCubicBez(NSVGrasterizer* r, - float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4, - int level, int type) -{ - float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; - float dx,dy,d2,d3; - - if (level > 10) return; - - x12 = (x1+x2)*0.5f; - y12 = (y1+y2)*0.5f; - x23 = (x2+x3)*0.5f; - y23 = (y2+y3)*0.5f; - x34 = (x3+x4)*0.5f; - y34 = (y3+y4)*0.5f; - x123 = (x12+x23)*0.5f; - y123 = (y12+y23)*0.5f; - - dx = x4 - x1; - dy = y4 - y1; - d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); - d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); - - if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { - nsvg__addPathPoint(r, x4, y4, type); - return; - } - - x234 = (x23+x34)*0.5f; - y234 = (y23+y34)*0.5f; - x1234 = (x123+x234)*0.5f; - y1234 = (y123+y234)*0.5f; - - nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); - nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); -} - -static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) -{ - int i, j; - NSVGpath* path; - - for (path = shape->paths; path != NULL; path = path->next) { - r->npoints = 0; - // Flatten path - nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); - for (i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); - } - // Close path - nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); - // Build edges - for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) - nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); - } -} - -enum NSVGpointFlags -{ - NSVG_PT_CORNER = 0x01, - NSVG_PT_BEVEL = 0x02, - NSVG_PT_LEFT = 0x04 -}; - -static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - float len = nsvg__normalize(&dx, &dy); - float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) -{ - float w = lineWidth * 0.5f; - float px = p->x, py = p->y; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - - nsvg__addEdge(r, lx, ly, rx, ry); - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) -{ - float w = lineWidth * 0.5f; - float px = p->x - dx*w, py = p->y - dy*w; - float dlx = dy, dly = -dx; - float lx = px - dlx*w, ly = py - dly*w; - float rx = px + dlx*w, ry = py + dly*w; - - nsvg__addEdge(r, lx, ly, rx, ry); - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -#ifndef NSVG_PI -#define NSVG_PI (3.14159265358979323846264338327f) -#endif - -static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) -{ - int i; - float w = lineWidth * 0.5f; - float px = p->x, py = p->y; - float dlx = dy, dly = -dx; - float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; - - for (i = 0; i < ncap; i++) { - float a = (float)i/(float)(ncap-1)*NSVG_PI; - float ax = cosf(a) * w, ay = sinf(a) * w; - float x = px - dlx*ax - dx*ay; - float y = py - dly*ax - dy*ay; - - if (i > 0) - nsvg__addEdge(r, prevx, prevy, x, y); - - prevx = x; - prevy = y; - - if (i == 0) { - lx = x; ly = y; - } else if (i == ncap-1) { - rx = x; ry = y; - } - } - - if (connect) { - nsvg__addEdge(r, left->x, left->y, lx, ly); - nsvg__addEdge(r, rx, ry, right->x, right->y); - } - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); - float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); - float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); - float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); - - nsvg__addEdge(r, lx0, ly0, left->x, left->y); - nsvg__addEdge(r, lx1, ly1, lx0, ly0); - - nsvg__addEdge(r, right->x, right->y, rx0, ry0); - nsvg__addEdge(r, rx0, ry0, rx1, ry1); - - left->x = lx1; left->y = ly1; - right->x = rx1; right->y = ry1; -} - -static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float lx0, rx0, lx1, rx1; - float ly0, ry0, ly1, ry1; - - if (p1->flags & NSVG_PT_LEFT) { - lx0 = lx1 = p1->x - p1->dmx * w; - ly0 = ly1 = p1->y - p1->dmy * w; - nsvg__addEdge(r, lx1, ly1, left->x, left->y); - - rx0 = p1->x + (dlx0 * w); - ry0 = p1->y + (dly0 * w); - rx1 = p1->x + (dlx1 * w); - ry1 = p1->y + (dly1 * w); - nsvg__addEdge(r, right->x, right->y, rx0, ry0); - nsvg__addEdge(r, rx0, ry0, rx1, ry1); - } else { - lx0 = p1->x - (dlx0 * w); - ly0 = p1->y - (dly0 * w); - lx1 = p1->x - (dlx1 * w); - ly1 = p1->y - (dly1 * w); - nsvg__addEdge(r, lx0, ly0, left->x, left->y); - nsvg__addEdge(r, lx1, ly1, lx0, ly0); - - rx0 = rx1 = p1->x + p1->dmx * w; - ry0 = ry1 = p1->y + p1->dmy * w; - nsvg__addEdge(r, right->x, right->y, rx1, ry1); - } - - left->x = lx1; left->y = ly1; - right->x = rx1; right->y = ry1; -} - -static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) -{ - int i, n; - float w = lineWidth * 0.5f; - float dlx0 = p0->dy, dly0 = -p0->dx; - float dlx1 = p1->dy, dly1 = -p1->dx; - float a0 = atan2f(dly0, dlx0); - float a1 = atan2f(dly1, dlx1); - float da = a1 - a0; - float lx, ly, rx, ry; - - if (da < NSVG_PI) da += NSVG_PI*2; - if (da > NSVG_PI) da -= NSVG_PI*2; - - n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); - if (n < 2) n = 2; - if (n > ncap) n = ncap; - - lx = left->x; - ly = left->y; - rx = right->x; - ry = right->y; - - for (i = 0; i < n; i++) { - float u = (float)i/(float)(n-1); - float a = a0 + u*da; - float ax = cosf(a) * w, ay = sinf(a) * w; - float lx1 = p1->x - ax, ly1 = p1->y - ay; - float rx1 = p1->x + ax, ry1 = p1->y + ay; - - nsvg__addEdge(r, lx1, ly1, lx, ly); - nsvg__addEdge(r, rx, ry, rx1, ry1); - - lx = lx1; ly = ly1; - rx = rx1; ry = ry1; - } - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) -{ - float w = lineWidth * 0.5f; - float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); - float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); - - nsvg__addEdge(r, lx, ly, left->x, left->y); - nsvg__addEdge(r, right->x, right->y, rx, ry); - - left->x = lx; left->y = ly; - right->x = rx; right->y = ry; -} - -static int nsvg__curveDivs(float r, float arc, float tol) -{ - float da = acosf(r / (r + tol)) * 2.0f; - int divs = (int)ceilf(arc / da); - if (divs < 2) divs = 2; - return divs; -} - -static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) -{ - int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. - NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; - NSVGpoint* p0, *p1; - int j, s, e; - - // Build stroke edges - if (closed) { - // Looping - p0 = &points[npoints-1]; - p1 = &points[0]; - s = 0; - e = npoints; - } else { - // Add cap - p0 = &points[0]; - p1 = &points[1]; - s = 1; - e = npoints-1; - } - - if (closed) { - nsvg__initClosed(&left, &right, p0, p1, lineWidth); - firstLeft = left; - firstRight = right; - } else { - // Add cap - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - nsvg__normalize(&dx, &dy); - if (lineCap == NSVG_CAP_BUTT) - nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); - else if (lineCap == NSVG_CAP_SQUARE) - nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); - else if (lineCap == NSVG_CAP_ROUND) - nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); - } - - for (j = s; j < e; ++j) { - if (p1->flags & NSVG_PT_CORNER) { - if (lineJoin == NSVG_JOIN_ROUND) - nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); - else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) - nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); - else - nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); - } else { - nsvg__straightJoin(r, &left, &right, p1, lineWidth); - } - p0 = p1++; - } - - if (closed) { - // Loop it - nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); - nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); - } else { - // Add cap - float dx = p1->x - p0->x; - float dy = p1->y - p0->y; - nsvg__normalize(&dx, &dy); - if (lineCap == NSVG_CAP_BUTT) - nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); - else if (lineCap == NSVG_CAP_SQUARE) - nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); - else if (lineCap == NSVG_CAP_ROUND) - nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); - } -} - -static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) -{ - int i, j; - NSVGpoint* p0, *p1; - - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - for (i = 0; i < r->npoints; i++) { - // Calculate segment direction and length - p0->dx = p1->x - p0->x; - p0->dy = p1->y - p0->y; - p0->len = nsvg__normalize(&p0->dx, &p0->dy); - // Advance - p0 = p1++; - } - - // calculate joins - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - for (j = 0; j < r->npoints; j++) { - float dlx0, dly0, dlx1, dly1, dmr2, cross; - dlx0 = p0->dy; - dly0 = -p0->dx; - dlx1 = p1->dy; - dly1 = -p1->dx; - // Calculate extrusions - p1->dmx = (dlx0 + dlx1) * 0.5f; - p1->dmy = (dly0 + dly1) * 0.5f; - dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; - if (dmr2 > 0.000001f) { - float s2 = 1.0f / dmr2; - if (s2 > 600.0f) { - s2 = 600.0f; - } - p1->dmx *= s2; - p1->dmy *= s2; - } - - // Clear flags, but keep the corner. - p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; - - // Keep track of left turns. - cross = p1->dx * p0->dy - p0->dx * p1->dy; - if (cross > 0.0f) - p1->flags |= NSVG_PT_LEFT; - - // Check to see if the corner needs to be beveled. - if (p1->flags & NSVG_PT_CORNER) { - if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { - p1->flags |= NSVG_PT_BEVEL; - } - } - - p0 = p1++; - } -} - -static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) -{ - int i, j, closed; - NSVGpath* path; - NSVGpoint* p0, *p1; - float miterLimit = shape->miterLimit; - int lineJoin = shape->strokeLineJoin; - int lineCap = shape->strokeLineCap; - float lineWidth = shape->strokeWidth * scale; - - for (path = shape->paths; path != NULL; path = path->next) { - // Flatten path - r->npoints = 0; - nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); - for (i = 0; i < path->npts-1; i += 3) { - float* p = &path->pts[i*2]; - nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); - } - if (r->npoints < 2) - continue; - - closed = path->closed; - - // If the first and last points are the same, remove the last, mark as closed path. - p0 = &r->points[r->npoints-1]; - p1 = &r->points[0]; - if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { - r->npoints--; - p0 = &r->points[r->npoints-1]; - closed = 1; - } - - if (shape->strokeDashCount > 0) { - int idash = 0, dashState = 1; - float totalDist = 0, dashLen, allDashLen, dashOffset; - NSVGpoint cur; - - if (closed) - nsvg__appendPathPoint(r, r->points[0]); - - // Duplicate points -> points2. - nsvg__duplicatePoints(r); - - r->npoints = 0; - cur = r->points2[0]; - nsvg__appendPathPoint(r, cur); - - // Figure out dash offset. - allDashLen = 0; - for (j = 0; j < shape->strokeDashCount; j++) - allDashLen += shape->strokeDashArray[j]; - if (shape->strokeDashCount & 1) - allDashLen *= 2.0f; - // Find location inside pattern - dashOffset = fmodf(shape->strokeDashOffset, allDashLen); - if (dashOffset < 0.0f) - dashOffset += allDashLen; - - while (dashOffset > shape->strokeDashArray[idash]) { - dashOffset -= shape->strokeDashArray[idash]; - idash = (idash + 1) % shape->strokeDashCount; - } - dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; - - for (j = 1; j < r->npoints2; ) { - float dx = r->points2[j].x - cur.x; - float dy = r->points2[j].y - cur.y; - float dist = sqrtf(dx*dx + dy*dy); - - if ((totalDist + dist) > dashLen) { - // Calculate intermediate point - float d = (dashLen - totalDist) / dist; - float x = cur.x + dx * d; - float y = cur.y + dy * d; - nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); - - // Stroke - if (r->npoints > 1 && dashState) { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); - } - // Advance dash pattern - dashState = !dashState; - idash = (idash+1) % shape->strokeDashCount; - dashLen = shape->strokeDashArray[idash] * scale; - // Restart - cur.x = x; - cur.y = y; - cur.flags = NSVG_PT_CORNER; - totalDist = 0.0f; - r->npoints = 0; - nsvg__appendPathPoint(r, cur); - } else { - totalDist += dist; - cur = r->points2[j]; - nsvg__appendPathPoint(r, cur); - j++; - } - } - // Stroke any leftover path - if (r->npoints > 1 && dashState) - nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); - } else { - nsvg__prepareStroke(r, miterLimit, lineJoin); - nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); - } - } -} - -static int nsvg__cmpEdge(const void *p, const void *q) -{ - const NSVGedge* a = (const NSVGedge*)p; - const NSVGedge* b = (const NSVGedge*)q; - - if (a->y0 < b->y0) return -1; - if (a->y0 > b->y0) return 1; - return 0; -} - - -static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) -{ - NSVGactiveEdge* z; - - if (r->freelist != NULL) { - // Restore from freelist. - z = r->freelist; - r->freelist = z->next; - } else { - // Alloc new edge. - z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); - if (z == NULL) return NULL; - } - - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); -// STBTT_assert(e->y0 <= start_point); - // round dx down to avoid going too far - if (dxdy < 0) - z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); - else - z->dx = (int)floorf(NSVG__FIX * dxdy); - z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); -// z->x -= off_x * FIX; - z->ey = e->y1; - z->next = 0; - z->dir = e->dir; - - return z; -} - -static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) -{ - z->next = r->freelist; - r->freelist = z; -} - -static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) -{ - int i = x0 >> NSVG__FIXSHIFT; - int j = x1 >> NSVG__FIXSHIFT; - if (i < *xmin) *xmin = i; - if (j > *xmax) *xmax = j; - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = (unsigned char)(scanline[i] + maxWeight); - } - } -} - -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) -{ - // non-zero winding fill - int x0 = 0, w = 0; - - if (fillRule == NSVG_FILLRULE_NONZERO) { - // Non-zero - while (e != NULL) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->dir; - } else { - int x1 = e->x; w += e->dir; - // if we went to zero, we need to draw - if (w == 0) - nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); - } - e = e->next; - } - } else if (fillRule == NSVG_FILLRULE_EVENODD) { - // Even-odd - while (e != NULL) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w = 1; - } else { - int x1 = e->x; w = 0; - nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); - } - e = e->next; - } - } -} - -static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } - -static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) -{ - return (r) | (g << 8) | (b << 16) | (a << 24); -} - -static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) -{ - int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); - int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; - int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; - int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; - int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; - return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); -} - -static unsigned int nsvg__applyOpacity(unsigned int c, float u) -{ - int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); - int r = (c) & 0xff; - int g = (c>>8) & 0xff; - int b = (c>>16) & 0xff; - int a = (((c>>24) & 0xff)*iu) >> 8; - return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); -} - -static inline int nsvg__div255(int x) -{ - return ((x+1) * 257) >> 16; -} - -static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, - float tx, float ty, float scale, NSVGcachedPaint* cache) -{ - - if (cache->type == NSVG_PAINT_COLOR) { - int i, cr, cg, cb, ca; - cr = cache->colors[0] & 0xff; - cg = (cache->colors[0] >> 8) & 0xff; - cb = (cache->colors[0] >> 16) & 0xff; - ca = (cache->colors[0] >> 24) & 0xff; - - for (i = 0; i < count; i++) { - int r,g,b; - int a = nsvg__div255((int)cover[0] * ca); - int ia = 255 - a; - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - } - } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { - // TODO: spread modes. - // TODO: plenty of opportunities to optimize. - float fx, fy, dx, gy; - float* t = cache->xform; - int i, cr, cg, cb, ca; - unsigned int c; - - fx = ((float)x - tx) / scale; - fy = ((float)y - ty) / scale; - dx = 1.0f / scale; - - for (i = 0; i < count; i++) { - int r,g,b,a,ia; - gy = fx*t[1] + fy*t[3] + t[5]; - c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; - cr = (c) & 0xff; - cg = (c >> 8) & 0xff; - cb = (c >> 16) & 0xff; - ca = (c >> 24) & 0xff; - - a = nsvg__div255((int)cover[0] * ca); - ia = 255 - a; - - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - fx += dx; - } - } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { - // TODO: spread modes. - // TODO: plenty of opportunities to optimize. - // TODO: focus (fx,fy) - float fx, fy, dx, gx, gy, gd; - float* t = cache->xform; - int i, cr, cg, cb, ca; - unsigned int c; - - fx = ((float)x - tx) / scale; - fy = ((float)y - ty) / scale; - dx = 1.0f / scale; - - for (i = 0; i < count; i++) { - int r,g,b,a,ia; - gx = fx*t[0] + fy*t[2] + t[4]; - gy = fx*t[1] + fy*t[3] + t[5]; - gd = sqrtf(gx*gx + gy*gy); - c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; - cr = (c) & 0xff; - cg = (c >> 8) & 0xff; - cb = (c >> 16) & 0xff; - ca = (c >> 24) & 0xff; - - a = nsvg__div255((int)cover[0] * ca); - ia = 255 - a; - - // Premultiply - r = nsvg__div255(cr * a); - g = nsvg__div255(cg * a); - b = nsvg__div255(cb * a); - - // Blend over - r += nsvg__div255(ia * (int)dst[0]); - g += nsvg__div255(ia * (int)dst[1]); - b += nsvg__div255(ia * (int)dst[2]); - a += nsvg__div255(ia * (int)dst[3]); - - dst[0] = (unsigned char)r; - dst[1] = (unsigned char)g; - dst[2] = (unsigned char)b; - dst[3] = (unsigned char)a; - - cover++; - dst += 4; - fx += dx; - } - } -} - -static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) -{ - NSVGactiveEdge *active = NULL; - int y, s; - int e = 0; - int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline - int xmin, xmax; - - for (y = 0; y < r->height; y++) { - memset(r->scanline, 0, r->width); - xmin = r->width; - xmax = 0; - for (s = 0; s < NSVG__SUBSAMPLES; ++s) { - // find center of pixel for this scanline - float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; - NSVGactiveEdge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - NSVGactiveEdge *z = *step; - if (z->ey <= scany) { - *step = z->next; // delete from list -// NSVG__assert(z->valid); - nsvg__freeActive(r, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for (;;) { - int changed = 0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - NSVGactiveEdge* t = *step; - NSVGactiveEdge* q = t->next; - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e < r->nedges && r->edges[e].y0 <= scany) { - if (r->edges[e].y1 > scany) { - NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); - if (z == NULL) break; - // find insertion point - if (active == NULL) { - active = z; - } else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - NSVGactiveEdge* p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - e++; - } - - // now process all active edges in non-zero fashion - if (active != NULL) - nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); - } - // Blit - if (xmin < 0) xmin = 0; - if (xmax > r->width-1) xmax = r->width-1; - if (xmin <= xmax) { - nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); - } - } - -} - -static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) -{ - int x,y; - - // Unpremultiply - for (y = 0; y < h; y++) { - unsigned char *row = &image[y*stride]; - for (x = 0; x < w; x++) { - int r = row[0], g = row[1], b = row[2], a = row[3]; - if (a != 0) { - row[0] = (unsigned char)(r*255/a); - row[1] = (unsigned char)(g*255/a); - row[2] = (unsigned char)(b*255/a); - } - row += 4; - } - } - - // Defringe - for (y = 0; y < h; y++) { - unsigned char *row = &image[y*stride]; - for (x = 0; x < w; x++) { - int r = 0, g = 0, b = 0, a = row[3], n = 0; - if (a == 0) { - if (x-1 > 0 && row[-1] != 0) { - r += row[-4]; - g += row[-3]; - b += row[-2]; - n++; - } - if (x+1 < w && row[7] != 0) { - r += row[4]; - g += row[5]; - b += row[6]; - n++; - } - if (y-1 > 0 && row[-stride+3] != 0) { - r += row[-stride]; - g += row[-stride+1]; - b += row[-stride+2]; - n++; - } - if (y+1 < h && row[stride+3] != 0) { - r += row[stride]; - g += row[stride+1]; - b += row[stride+2]; - n++; - } - if (n > 0) { - row[0] = (unsigned char)(r/n); - row[1] = (unsigned char)(g/n); - row[2] = (unsigned char)(b/n); - } - } - row += 4; - } - } -} - - -static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) -{ - int i, j; - NSVGgradient* grad; - - cache->type = paint->type; - - if (paint->type == NSVG_PAINT_COLOR) { - cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); - return; - } - - grad = paint->gradient; - - cache->spread = grad->spread; - memcpy(cache->xform, grad->xform, sizeof(float)*6); - - if (grad->nstops == 0) { - for (i = 0; i < 256; i++) - cache->colors[i] = 0; - } if (grad->nstops == 1) { - for (i = 0; i < 256; i++) - cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); - } else { - unsigned int ca, cb = 0; - float ua, ub, du, u; - int ia, ib, count; - - ca = nsvg__applyOpacity(grad->stops[0].color, opacity); - ua = nsvg__clampf(grad->stops[0].offset, 0, 1); - ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - for (i = 0; i < ia; i++) { - cache->colors[i] = ca; - } - - for (i = 0; i < grad->nstops-1; i++) { - ca = nsvg__applyOpacity(grad->stops[i].color, opacity); - cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); - ua = nsvg__clampf(grad->stops[i].offset, 0, 1); - ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); - ia = (int)(ua * 255.0f); - ib = (int)(ub * 255.0f); - count = ib - ia; - if (count <= 0) continue; - u = 0; - du = 1.0f / (float)count; - for (j = 0; j < count; j++) { - cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); - u += du; - } - } - - for (i = ib; i < 256; i++) - cache->colors[i] = cb; - } - -} - -/* -static void dumpEdges(NSVGrasterizer* r, const char* name) -{ - float xmin = 0, xmax = 0, ymin = 0, ymax = 0; - NSVGedge *e = NULL; - int i; - if (r->nedges == 0) return; - FILE* fp = fopen(name, "w"); - if (fp == NULL) return; - - xmin = xmax = r->edges[0].x0; - ymin = ymax = r->edges[0].y0; - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - xmin = nsvg__minf(xmin, e->x0); - xmin = nsvg__minf(xmin, e->x1); - xmax = nsvg__maxf(xmax, e->x0); - xmax = nsvg__maxf(xmax, e->x1); - ymin = nsvg__minf(ymin, e->y0); - ymin = nsvg__minf(ymin, e->y1); - ymax = nsvg__maxf(ymax, e->y0); - ymax = nsvg__maxf(ymax, e->y1); - } - - fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); - - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); - } - - for (i = 0; i < r->npoints; i++) { - if (i+1 < r->npoints) - fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); - fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); - } - - fprintf(fp, ""); - fclose(fp); -} -*/ - -void nsvgRasterize(NSVGrasterizer* r, - NSVGimage* image, float tx, float ty, float scale, - unsigned char* dst, int w, int h, int stride) -{ - NSVGshape *shape = NULL; - NSVGedge *e = NULL; - NSVGcachedPaint cache; - int i; - - r->bitmap = dst; - r->width = w; - r->height = h; - r->stride = stride; - - if (w > r->cscanline) { - r->cscanline = w; - r->scanline = (unsigned char*)realloc(r->scanline, w); - if (r->scanline == NULL) return; - } - - for (i = 0; i < h; i++) - memset(&dst[i*stride], 0, w*4); - - for (shape = image->shapes; shape != NULL; shape = shape->next) { - if (!(shape->flags & NSVG_FLAGS_VISIBLE)) - continue; - - if (shape->fill.type != NSVG_PAINT_NONE) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShape(r, shape, scale); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaint(&cache, &shape->fill, shape->opacity); - - nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); - } - if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { - nsvg__resetPool(r); - r->freelist = NULL; - r->nedges = 0; - - nsvg__flattenShapeStroke(r, shape, scale); - -// dumpEdges(r, "edge.svg"); - - // Scale and translate edges - for (i = 0; i < r->nedges; i++) { - e = &r->edges[i]; - e->x0 = tx + e->x0; - e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; - e->x1 = tx + e->x1; - e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; - } - - // Rasterize edges - qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); - - // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule - nsvg__initPaint(&cache, &shape->stroke, shape->opacity); - - nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); - } - } - - nsvg__unpremultiplyAlpha(dst, w, h, stride); - - r->bitmap = NULL; - r->width = 0; - r->height = 0; - r->stride = 0; -} - -#endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 29b8b7e73..430da0c34 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -255,6 +255,8 @@ set(SLIC3R_GUI_SOURCES Utils/WinRegistry.hpp ) +find_package(NanoSVG REQUIRED) + if (APPLE) list(APPEND SLIC3R_GUI_SOURCES Utils/RetinaHelperImpl.mm @@ -279,7 +281,7 @@ endforeach() encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude libcereal imgui GLEW::GLEW OpenGL::GL hidapi libcurl ${wxWidgets_LIBRARIES} NanoSVG::nanosvg NanoSVG::nanosvgrast) if (MSVC) target_link_libraries(libslic3r_gui Setupapi.lib) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index b585798d3..844f6dea9 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -15,10 +15,8 @@ #include #endif /* __WXGTK2__ */ -//#define NANOSVG_IMPLEMENTATION -#include "nanosvg/nanosvg.h" -#define NANOSVGRAST_IMPLEMENTATION -#include "nanosvg/nanosvgrast.h" +#include +#include namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 6065f22a5..9c039376d 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -23,8 +23,8 @@ #define STB_DXT_IMPLEMENTATION #include "stb_dxt/stb_dxt.h" -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" +#include +#include #include "libslic3r/Utils.hpp" diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 5ab4685bf..9aa8833ff 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -37,9 +37,8 @@ #endif // ENABLE_GL_IMGUI_SHADERS #include "../Utils/MacDarkMode.hpp" - -#include "nanosvg/nanosvg.h" -#include "nanosvg/nanosvgrast.h" +#include +#include namespace Slic3r { namespace GUI { From 2ab64819aabdc28661fcc4e552c05e91d9366bc0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 26 Apr 2022 15:53:28 +0200 Subject: [PATCH 39/93] Fixes to support wxWidgets 3.1.6(7) --- src/slic3r/GUI/AboutDialog.cpp | 9 ++++--- src/slic3r/GUI/BitmapComboBox.cpp | 4 ++- src/slic3r/GUI/GUI_App.cpp | 3 ++- src/slic3r/GUI/GUI_Factories.cpp | 14 ++++++++--- src/slic3r/GUI/GUI_Factories.hpp | 4 ++- src/slic3r/GUI/KBShortcutsDialog.cpp | 10 +++++--- src/slic3r/GUI/MsgDialog.cpp | 8 ++++++ src/slic3r/GUI/ObjectDataViewModel.cpp | 2 +- src/slic3r/GUI/PresetComboBoxes.cpp | 7 +++--- src/slic3r/GUI/SysInfoDialog.cpp | 10 +++++--- src/slic3r/GUI/Tab.cpp | 7 ++++-- src/slic3r/GUI/wxExtensions.cpp | 35 ++++++++++++++++++-------- src/slic3r/GUI/wxExtensions.hpp | 7 ++++-- 13 files changed, 83 insertions(+), 37 deletions(-) diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index e444fb03c..0fe75d453 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -221,8 +221,9 @@ AboutDialog::AboutDialog() main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20); // logo - m_logo_bitmap = ScalableBitmap(this, wxGetApp().logo_name(), 192); - m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp()); +// m_logo_bitmap = ScalableBitmap(this, wxGetApp().logo_name(), 192); +// m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp()); + m_logo = new wxStaticBitmap(this, wxID_ANY, wxBitmapBundle::FromSVGFile(Slic3r::var(wxGetApp().logo_name()+".svg"), wxSize(192, 192))); hsizer->Add(m_logo, 1, wxALIGN_CENTER_VERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); @@ -322,8 +323,8 @@ AboutDialog::AboutDialog() void AboutDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_logo_bitmap.msw_rescale(); - m_logo->SetBitmap(m_logo_bitmap.bmp()); +// m_logo_bitmap.msw_rescale(); +// m_logo->SetBitmap(m_logo_bitmap.bmp()); const wxFont& font = GetFont(); const int fs = font.GetPointSize() - 1; diff --git a/src/slic3r/GUI/BitmapComboBox.cpp b/src/slic3r/GUI/BitmapComboBox.cpp index 3396c627b..54e1a31fa 100644 --- a/src/slic3r/GUI/BitmapComboBox.cpp +++ b/src/slic3r/GUI/BitmapComboBox.cpp @@ -166,7 +166,8 @@ int BitmapComboBox::Append(const wxString& item) //2. But then set width to 0 value for no using of bitmap left and right spacing //3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct - wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1)); +// wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1)); + wxBitmap bitmap(1, 16); { // bitmap.SetWidth(0); is depricated now // so, use next code @@ -268,6 +269,7 @@ void BitmapComboBox::DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED( void BitmapComboBox::Rescale() { + return; // Next workaround: To correct scaling of a BitmapCombobox // we need to refill control with new bitmaps const wxString selection = this->GetValue(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 9a4921a84..4b0544488 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2088,6 +2088,7 @@ bool GUI_App::load_language(wxString language, bool initial) { // Allocating a temporary locale will switch the default wxTranslations to its internal wxTranslations instance. wxLocale temp_locale; + temp_locale.Init(); // Set the current translation's language to default, otherwise GetBestTranslation() may not work (see the wxWidgets source code). wxTranslations::Get()->SetLanguage(wxLANGUAGE_DEFAULT); // Let the wxFileTranslationsLoader enumerate all translation dictionaries for PrusaSlicer @@ -2228,7 +2229,7 @@ void GUI_App::update_mode() { sidebar().update_mode(); -#ifdef _MSW_DARK_MODE +#ifdef _WIN32 //_MSW_DARK_MODE if (!wxGetApp().tabs_as_menu()) dynamic_cast(mainframe->m_tabpanel)->UpdateMode(); #endif diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 7b3476d71..4adb161c2 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -142,13 +142,20 @@ std::map SettingsFactory::CATEGORY_ICON = { L("Hollowing") , "hollowing" } }; -wxBitmap SettingsFactory::get_category_bitmap(const std::string& category_name, bool menu_bmp /*= true*/) +//wxBitmap SettingsFactory::get_category_bitmap(const std::string& category_name, bool menu_bmp /*= true*/) +wxBitmap SettingsFactory::get_category_bitmap_(const std::string& category_name, bool menu_bmp /*= true*/) { if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) return wxNullBitmap; - return menu_bmp ? create_menu_bitmap(CATEGORY_ICON.at(category_name)) : create_scaled_bitmap(CATEGORY_ICON.at(category_name)); + return /*menu_bmp ? create_menu_bitmap(CATEGORY_ICON.at(category_name)) : */create_scaled_bitmap(CATEGORY_ICON.at(category_name)); } +wxBitmapBundle SettingsFactory::get_category_bitmap(const std::string& category_name) +{ + if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) + return wxNullBitmap; + return create_menu_bitmap(CATEGORY_ICON.at(category_name)); +} //------------------------------------- // MenuFactory @@ -435,7 +442,8 @@ std::vector MenuFactory::get_volume_bitmaps() std::vector volume_bmps; volume_bmps.reserve(ADD_VOLUME_MENU_ITEMS.size()); for (auto item : ADD_VOLUME_MENU_ITEMS) - volume_bmps.push_back(create_menu_bitmap(item.second)); +// volume_bmps.push_back(create_menu_bitmap(item.second)); + volume_bmps.push_back(create_scaled_bitmap(item.second, nullptr, 16, false, "", true)); return volume_bmps; } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 0c478a97b..cba828c29 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -25,7 +25,9 @@ struct SettingsFactory typedef std::map> Bundle; static std::map CATEGORY_ICON; - static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true); +// static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true); + static wxBitmap get_category_bitmap_(const std::string& category_name, bool menu_bmp = true); + static wxBitmapBundle get_category_bitmap(const std::string& category_name); static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings); static std::vector get_options(bool is_part); }; diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index ba3f6675f..a749ad405 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -57,8 +57,8 @@ KBShortcutsDialog::KBShortcutsDialog() void KBShortcutsDialog::on_dpi_changed(const wxRect& suggested_rect) { - m_logo_bmp.msw_rescale(); - m_header_bitmap->SetBitmap(m_logo_bmp.bmp()); + //m_logo_bmp.msw_rescale(); + //m_header_bitmap->SetBitmap(m_logo_bmp.bmp()); msw_buttons_rescale(this, em_unit(), { wxID_OK }); Layout(); @@ -270,8 +270,10 @@ wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_f sizer->AddStretchSpacer(); // logo - m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 32); - m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp()); + //m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 32); + //m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp()); + m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, wxBitmapBundle::FromSVGFile(Slic3r::var(wxGetApp().logo_name() + ".svg"), wxSize(32, 32))); + sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); // text diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 94e9ca5f3..76bcfdd4a 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -99,9 +99,17 @@ void MsgDialog::apply_style(long style) if (style & wxNO) add_button(wxID_NO, (style & wxNO_DEFAULT)); if (style & wxCANCEL) add_button(wxID_CANCEL, (style & wxCANCEL_DEFAULT)); +#if 0 logo->SetBitmap( create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" : style & wxICON_INFORMATION ? "info" : style & wxICON_QUESTION ? "question" : "PrusaSlicer", this, 64, style & wxICON_ERROR)); +#else + std::string icon_name = style & wxICON_WARNING ? "exclamation" : + style & wxICON_INFORMATION ? "info" : + style & wxICON_QUESTION ? "question" : "PrusaSlicer"; + icon_name += ".svg"; + logo->SetBitmap(wxBitmapBundle::FromSVGFile(Slic3r::var(icon_name), wxSize(64, 64))); +#endif } void MsgDialog::finalize() diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 496cdcfc7..4344deb24 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -191,7 +191,7 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps() if (bmp == nullptr) { std::vector bmps; for (auto& category : m_opt_categories) - bmps.emplace_back(SettingsFactory::get_category_bitmap(category, false)); + bmps.emplace_back(SettingsFactory::get_category_bitmap_(category, false)); bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index ed4888a87..def48a1e4 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -376,8 +376,8 @@ void PresetComboBox::msw_rescale() { m_em_unit = em_unit(this); - m_bitmapIncompatible.msw_rescale(); - m_bitmapCompatible.msw_rescale(); + //m_bitmapIncompatible.msw_rescale(); + //m_bitmapCompatible.msw_rescale(); // parameters for an icon's drawing fill_width_height(); @@ -403,7 +403,8 @@ void PresetComboBox::fill_width_height() * So set sizes for solid_colored icons used for filament preset * and scale them in respect to em_unit value */ - const float scale_f = (float)m_em_unit * 0.1f; +// const float scale_f = (float)m_em_unit * 0.1f; + const float scale_f = 1.0f; thin_icon_width = lroundf(8 * scale_f); // analogue to 8px; wide_icon_width = norm_icon_width + thin_icon_width; diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index 53e7d637d..db8cfc9c2 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -102,8 +102,10 @@ SysInfoDialog::SysInfoDialog() main_sizer->Add(hsizer, 1, wxEXPAND | wxALL, 10); // logo - m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 192); - m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bmp.bmp()); + //m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 192); + //m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bmp.bmp()); + m_logo = new wxStaticBitmap(this, wxID_ANY, wxBitmapBundle::FromSVGFile(Slic3r::var(wxGetApp().logo_name() + ".svg"), wxSize(192, 192))); + hsizer->Add(m_logo, 0, wxALIGN_CENTER_VERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); @@ -194,8 +196,8 @@ SysInfoDialog::SysInfoDialog() void SysInfoDialog::on_dpi_changed(const wxRect &suggested_rect) { - m_logo_bmp.msw_rescale(); - m_logo->SetBitmap(m_logo_bmp.bmp()); + //m_logo_bmp.msw_rescale(); + //m_logo->SetBitmap(m_logo_bmp.bmp()); wxFont font = get_default_font(this); const int fs = font.GetPointSize() - 1; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 96151d8c7..1cb6ec8fc 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -772,8 +772,11 @@ void Tab::update_changed_tree_ui() void Tab::update_undo_buttons() { - m_undo_btn-> SetBitmap_(m_is_modified_values ? m_bmp_value_revert: m_bmp_white_bullet); - m_undo_to_sys_btn-> SetBitmap_(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock); + m_undo_btn-> SetBitmap_(m_is_modified_values ? m_bmp_value_revert.name(): m_bmp_white_bullet.name()); + m_undo_to_sys_btn-> SetBitmap_(m_is_nonsys_values ? m_bmp_non_system->name() : m_bmp_value_lock.name()); + + //m_undo_btn-> SetBitmap_(m_is_modified_values ? m_bmp_value_revert: m_bmp_white_bullet); + //m_undo_to_sys_btn-> SetBitmap_(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock); m_undo_btn->SetToolTip(m_is_modified_values ? m_ttg_value_revert : m_ttg_white_bullet); m_undo_to_sys_btn->SetToolTip(m_is_nonsys_values ? *m_ttg_non_system : m_ttg_value_lock); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 876398510..9326c9258 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -16,6 +16,7 @@ #include "Plater.hpp" #include "../Utils/MacDarkMode.hpp" #include "BitmapComboBox.hpp" +#include "libslic3r/Utils.hpp" #include "OG_CustomCtrl.hpp" #include "libslic3r/Color.hpp" @@ -26,11 +27,13 @@ static std::map msw_menuitem_bitmaps; #ifdef __WXMSW__ void msw_rescale_menu(wxMenu* menu) { + return; struct update_icons { static void run(wxMenuItem* item) { const auto it = msw_menuitem_bitmaps.find(item->GetId()); if (it != msw_menuitem_bitmaps.end()) { - const wxBitmap& item_icon = create_menu_bitmap(it->second); +// const wxBitmap& item_icon = create_menu_bitmap(it->second); + const wxBitmapBundle& item_icon = create_menu_bitmap(it->second); if (item_icon.IsOk()) item->SetBitmap(item_icon); } @@ -63,7 +66,8 @@ void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condi } wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler, +// std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler, + std::function cb, const wxBitmapBundle& icon, wxEvtHandler* event_handler, std::function const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/) { if (id == wxID_ANY) @@ -100,7 +104,9 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const if (id == wxID_ANY) id = wxNewId(); - const wxBitmap& bmp = !icon.empty() ? create_menu_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr +// const wxBitmap& bmp = !icon.empty() ? create_menu_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr + const wxBitmapBundle& bmp = !icon.empty() ? create_menu_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr + //#ifdef __WXMSW__ #ifndef __WXGTK__ if (bmp.IsOk()) @@ -420,9 +426,11 @@ int mode_icon_px_size() #endif } -wxBitmap create_menu_bitmap(const std::string& bmp_name) +//wxBitmap create_menu_bitmap(const std::string& bmp_name) +wxBitmapBundle create_menu_bitmap(const std::string& bmp_name) { - return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true); + // return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true); + return wxBitmapBundle::FromSVGFile(Slic3r::var(bmp_name + ".svg"), wxSize(16, 16)); } // win is used to get a correct em_unit value @@ -601,6 +609,7 @@ void LockButton::SetLock(bool lock) void LockButton::msw_rescale() { + return; m_bmp_lock_closed.msw_rescale(); m_bmp_lock_closed_f.msw_rescale(); m_bmp_lock_open.msw_rescale(); @@ -639,7 +648,8 @@ ModeButton::ModeButton( wxWindow* parent, const wxString& mode/* = wxEmptyString*/, const std::string& icon_name/* = ""*/, int px_cnt/* = 16*/) : - ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT) +// ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT) + ScalableButton(parent, wxID_ANY, icon_name, mode, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT) { Init(mode); } @@ -852,9 +862,10 @@ ScalableButton::ScalableButton( wxWindow * parent, Slic3r::GUI::wxGetApp().UpdateDarkUI(this); if (!icon_name.empty()) { - SetBitmap(create_scaled_bitmap(icon_name, parent, m_px_cnt)); - if (m_use_default_disabled_bitmap) - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); +// SetBitmap(create_scaled_bitmap(icon_name, parent, m_px_cnt)); + SetBitmap(wxBitmapBundle::FromSVGFile(Slic3r::var(icon_name + ".svg"), wxSize(m_px_cnt, m_px_cnt))); + //if (m_use_default_disabled_bitmap) + // SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); if (!label.empty()) SetBitmapMargins(int(0.5* em_unit(parent)), 0); } @@ -896,7 +907,8 @@ bool ScalableButton::SetBitmap_(const std::string& bmp_name) if (m_current_icon_name.empty()) return false; - wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); +// wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); + wxBitmapBundle bmp = wxBitmapBundle::FromSVGFile(Slic3r::var(m_current_icon_name + ".svg"), wxSize(16, 16)); SetBitmap(bmp); SetBitmapCurrent(bmp); SetBitmapPressed(bmp); @@ -931,7 +943,8 @@ void ScalableButton::msw_rescale() { Slic3r::GUI::wxGetApp().UpdateDarkUI(this, m_has_border); - if (!m_current_icon_name.empty()) { +// if (!m_current_icon_name.empty()) { + if (0) { wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); SetBitmap(bmp); SetBitmapCurrent(bmp); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index f3921919e..8387551f2 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -22,7 +23,8 @@ inline void msw_rescale_menu(wxMenu* /* menu */) {} #endif /* __WXMSW__ */ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, - std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler = nullptr, +// std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler = nullptr, + std::function cb, const wxBitmapBundle& icon, wxEvtHandler* event_handler = nullptr, std::function const cb_condition = []() { return true;}, wxWindow* parent = nullptr, int insert_pos = wxNOT_FOUND); wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr, @@ -49,7 +51,8 @@ void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector< int em_unit(wxWindow* win); int mode_icon_px_size(); -wxBitmap create_menu_bitmap(const std::string& bmp_name); +//wxBitmap create_menu_bitmap(const std::string& bmp_name); +wxBitmapBundle create_menu_bitmap(const std::string& bmp_name); wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr, const int px_cnt = 16, const bool grayscale = false, From dd6f7a71f1aaf95b93270a14073d1a5239669d5b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 10 May 2022 12:24:04 +0200 Subject: [PATCH 40/93] Using of wxWidgets 3.1.6 WIP: * Create Cache of wxBitmapBundles instead of wxBitmaps * Use wxBitmapBundles instead of wxBitmap for most of Widgets * Use empty bitmabundles instead of wxNullBitmap for wxBitmapComboBoxes. * Updated wxWidgets.cmake * OSX specific: Discard BitmapComboBox overrides + some code cleaning --- src/slic3r/GUI/AboutDialog.cpp | 4 +- src/slic3r/GUI/BitmapCache.cpp | 327 +++++++++++++++++++++- src/slic3r/GUI/BitmapCache.hpp | 21 +- src/slic3r/GUI/BitmapComboBox.cpp | 92 +----- src/slic3r/GUI/BitmapComboBox.hpp | 17 +- src/slic3r/GUI/ButtonsDescription.cpp | 3 - src/slic3r/GUI/ConfigWizard.cpp | 24 +- src/slic3r/GUI/ConfigWizard_private.hpp | 2 +- src/slic3r/GUI/DoubleSlider.cpp | 101 +++---- src/slic3r/GUI/ExtraRenderers.cpp | 2 +- src/slic3r/GUI/ExtruderSequenceDialog.cpp | 3 - src/slic3r/GUI/Field.hpp | 16 +- src/slic3r/GUI/GUI_App.cpp | 4 +- src/slic3r/GUI/GUI_Factories.cpp | 42 +-- src/slic3r/GUI/GUI_Factories.hpp | 7 +- src/slic3r/GUI/GUI_ObjectLayers.cpp | 68 ++--- src/slic3r/GUI/GUI_ObjectList.cpp | 8 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 41 +-- src/slic3r/GUI/GUI_ObjectSettings.cpp | 13 +- src/slic3r/GUI/GUI_ObjectSettings.hpp | 1 - src/slic3r/GUI/GalleryDialog.cpp | 44 ++- src/slic3r/GUI/KBShortcutsDialog.cpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 9 +- src/slic3r/GUI/MsgDialog.cpp | 11 +- src/slic3r/GUI/Notebook.cpp | 12 +- src/slic3r/GUI/Notebook.hpp | 6 + src/slic3r/GUI/OG_CustomCtrl.cpp | 42 +-- src/slic3r/GUI/OG_CustomCtrl.hpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 84 +++--- src/slic3r/GUI/ObjectDataViewModel.hpp | 38 +-- src/slic3r/GUI/OptionsGroup.cpp | 3 +- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 27 +- src/slic3r/GUI/PhysicalPrinterDialog.hpp | 5 +- src/slic3r/GUI/Plater.cpp | 41 +-- src/slic3r/GUI/PresetComboBoxes.cpp | 177 ++++++------ src/slic3r/GUI/PresetComboBoxes.hpp | 14 +- src/slic3r/GUI/SavePresetDialog.cpp | 4 +- src/slic3r/GUI/Search.cpp | 11 +- src/slic3r/GUI/Search.hpp | 2 +- src/slic3r/GUI/SysInfoDialog.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 116 +++----- src/slic3r/GUI/Tab.hpp | 6 - src/slic3r/GUI/UnsavedChangesDialog.cpp | 58 ++-- src/slic3r/GUI/wxExtensions.cpp | 223 +++++---------- src/slic3r/GUI/wxExtensions.hpp | 60 ++-- 45 files changed, 930 insertions(+), 867 deletions(-) diff --git a/src/slic3r/GUI/AboutDialog.cpp b/src/slic3r/GUI/AboutDialog.cpp index 0fe75d453..abd137941 100644 --- a/src/slic3r/GUI/AboutDialog.cpp +++ b/src/slic3r/GUI/AboutDialog.cpp @@ -221,9 +221,7 @@ AboutDialog::AboutDialog() main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20); // logo -// m_logo_bitmap = ScalableBitmap(this, wxGetApp().logo_name(), 192); -// m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bitmap.bmp()); - m_logo = new wxStaticBitmap(this, wxID_ANY, wxBitmapBundle::FromSVGFile(Slic3r::var(wxGetApp().logo_name()+".svg"), wxSize(192, 192))); + m_logo = new wxStaticBitmap(this, wxID_ANY, *get_bmp_bundle(wxGetApp().logo_name(), 192)); hsizer->Add(m_logo, 1, wxALIGN_CENTER_VERTICAL); wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 844f6dea9..d2bf98b5b 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -60,7 +60,164 @@ static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image, float scale = 1. #endif } -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height) +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vector& bmps) +{ + wxVector bitmaps; + + std::set scales = {1.0}; +#ifdef __APPLE__ + scales.emplace(m_scale); +#else + size_t disp_cnt = wxDisplay::GetCount(); + for (size_t disp = 0; disp < disp_cnt; ++disp) + scales.emplace(wxDisplay(disp).GetScaleFactor()); +#endif + + for (double scale : scales) { + size_t width = 0; + size_t height = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { +#ifdef __APPLE__ + wxSize size = bmp_bndl->GetPreferredBitmapSizeAtScale(1.0); +#else + wxSize size = bmp_bndl->GetPreferredBitmapSizeAtScale(scale); +#endif + width += size.GetWidth(); + height = std::max(height, size.GetHeight()); + } + + std::string bitmap_key = name + "," +float_to_string_decimal_point(scale); + +#ifdef __WXGTK2__ + // Broken alpha workaround + wxImage image(width, height); + image.InitAlpha(); + // Fill in with a white color. + memset(image.GetData(), 0x0ff, width * height * 3); + // Fill in with full transparency. + memset(image.GetAlpha(), 0, width * height); + size_t x = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetPreferredBitmapSizeAtScale(scale)); + if (bmp.GetWidth() > 0) { + if (bmp.GetDepth() == 32) { + wxAlphaPixelData data(bmp); + //FIXME The following method is missing from wxWidgets 3.1.1. + // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). + //data.UseAlpha(); + if (data) { + for (int r = 0; r < bmp.GetHeight(); ++r) { + wxAlphaPixelData::Iterator src(data); + src.Offset(data, 0, r); + unsigned char* dst_pixels = image.GetData() + (x + r * width) * 3; + unsigned char* dst_alpha = image.GetAlpha() + x + r * width; + for (int c = 0; c < bmp.GetWidth(); ++c, ++src) { + *dst_pixels++ = src.Red(); + *dst_pixels++ = src.Green(); + *dst_pixels++ = src.Blue(); + *dst_alpha++ = src.Alpha(); + } + } + } + } + else if (bmp.GetDepth() == 24) { + wxNativePixelData data(bmp); + if (data) { + for (int r = 0; r < bmp.GetHeight(); ++r) { + wxNativePixelData::Iterator src(data); + src.Offset(data, 0, r); + unsigned char* dst_pixels = image.GetData() + (x + r * width) * 3; + unsigned char* dst_alpha = image.GetAlpha() + x + r * width; + for (int c = 0; c < bmp.GetWidth(); ++c, ++src) { + *dst_pixels++ = src.Red(); + *dst_pixels++ = src.Green(); + *dst_pixels++ = src.Blue(); + *dst_alpha++ = wxALPHA_OPAQUE; + } + } + } + } + } + x += bmp.GetWidth(); + } + + bitmaps.push_back(* this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)))); + +#else + + wxBitmap* bitmap = this->insert(bitmap_key, width, height, scale); + wxMemoryDC memDC; + memDC.SelectObject(*bitmap); + memDC.SetBackground(*wxTRANSPARENT_BRUSH); + memDC.Clear(); + size_t x = 0; + for (const wxBitmapBundle* bmp_bndl : bmps) { + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetPreferredBitmapSizeAtScale(scale)); + + if (bmp.GetWidth() > 0) + memDC.DrawBitmap(bmp, x, 0, true); +#ifdef __APPLE__ + // we should "move" with step equal to non-scaled width + x += bmp.GetScaledWidth(); +#else + x += bmp.GetWidth(); +#endif + } + memDC.SelectObject(wxNullBitmap); + bitmaps.push_back(*bitmap); + +#endif + } + + return insert_bndl(name, bitmaps); +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string &bitmap_key, const char* data, size_t width, size_t height) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(wxBitmapBundle::FromSVG(data, wxSize(width, height))); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle::FromSVG(data, wxSize(width, height)); + } + return bndl; +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& bitmap_key, const wxBitmapBundle& bmp) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(bmp); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle(bmp); + } + return bndl; +} + +wxBitmapBundle* BitmapCache::insert_bndl(const std::string& bitmap_key, const wxVector& bmps) +{ + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + bndl = new wxBitmapBundle(wxBitmapBundle::FromBitmaps(bmps)); + m_bndl_map[bitmap_key] = bndl; + } + else { + bndl = it->second; + *bndl = wxBitmapBundle::FromBitmaps(bmps); + } + return bndl; +} + +wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height, double scale/* = -1.0*/) { wxBitmap *bitmap = nullptr; auto it = m_map.find(bitmap_key); @@ -76,7 +233,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_ // So, We need to let the Mac OS wxBitmap implementation // know that the image may already be scaled appropriately for Retina, // and thereby that it's not supposed to upscale it. - bitmap->CreateScaled(width, height, -1, m_scale); + bitmap->CreateScaled(width, height, -1, scale < 0.0 ? m_scale : scale); #endif m_map[bitmap_key] = bitmap; } else { @@ -297,6 +454,93 @@ error: return NULL; } +void BitmapCache::nsvgGetDataFromFileWithReplace(const char* filename, std::string& data_str, const std::map& replaces) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + + fp = boost::nowide::fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size + 1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + + data_str.assign(data); + for (auto val : replaces) + boost::replace_all(data_str, val.first, val.second); + + free(data); + return; + +error: + if (fp) fclose(fp); + if (data) free(data); + return; +} + +wxBitmapBundle* BitmapCache::from_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, + const bool dark_mode, const std::string& new_color /*= ""*/) +{ + if (target_width == 0) + target_width = target_height; + std::string bitmap_key = bitmap_name + (target_height != 0 ? + "-h" + std::to_string(target_height) : + "-w" + std::to_string(target_width)) +// + (m_scale != 1.0f ? "-s" + float_to_string_decimal_point(m_scale) : "") + + (dark_mode ? "-dm" : "") + + new_color; + + auto it = m_bndl_map.find(bitmap_key); + if (it != m_bndl_map.end()) + return it->second; + + // map of color replaces + std::map replaces; + if (dark_mode) + replaces["\"#808080\""] = "\"#FFFFFF\""; + if (!new_color.empty()) + replaces["\"#ED6B21\""] = "\"" + new_color + "\""; + + std::string str; + nsvgGetDataFromFileWithReplace(Slic3r::var(bitmap_name + ".svg").c_str(), str, replaces); + if (str.empty()) + return nullptr; + + return insert_bndl(bitmap_key, str.data(), target_width, target_height); +} + +wxBitmapBundle* BitmapCache::from_png(const std::string& bitmap_name, unsigned width, unsigned height) +{ + std::string bitmap_key = bitmap_name + (height != 0 ? + "-h" + std::to_string(height) : + "-w" + std::to_string(width)); + + auto it = m_bndl_map.find(bitmap_key); + if (it != m_bndl_map.end()) + return it->second; + + wxImage image; + if (!image.LoadFile(Slic3r::GUI::from_u8(Slic3r::var(bitmap_name + ".png")), wxBITMAP_TYPE_PNG) || + image.GetWidth() == 0 || image.GetHeight() == 0) + return nullptr; + + if (height != 0 && unsigned(image.GetHeight()) != height) + width = unsigned(0.5f + float(image.GetWidth()) * height / image.GetHeight()); + else if (width != 0 && unsigned(image.GetWidth()) != width) + height = unsigned(0.5f + float(image.GetHeight()) * width / image.GetWidth()); + + if (height != 0 && width != 0) + image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); + + return this->insert_bndl(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); +} + wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, const bool grayscale/* = false*/, const bool dark_mode/* = false*/, const std::string& new_color /*= ""*/) { @@ -395,5 +639,84 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi return wxImage_to_wxBitmap_with_alpha(std::move(image), scale); } +//we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap +wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/) +{ + wxVector bitmaps; + + std::set scales = { 1.0 }; +#ifdef __APPLE__ + scales.emplace(m_scale); +#else + size_t disp_cnt = wxDisplay::GetCount(); + for (size_t disp = 0; disp < disp_cnt; ++disp) + scales.emplace(wxDisplay(disp).GetScaleFactor()); +#endif + + for (double scale : scales) { + size_t width = width_in * scale; + size_t height = height_in * scale; + + wxImage image(width, height); + image.InitAlpha(); + unsigned char* imgdata = image.GetData(); + unsigned char* imgalpha = image.GetAlpha(); + for (size_t i = 0; i < width * height; ++i) { + *imgdata++ = r; + *imgdata++ = g; + *imgdata++ = b; + *imgalpha++ = transparency; + } + + // Add border, make white/light spools easier to see + if (border_width > 0) { + + // Restrict to width of image + if (border_width > height) border_width = height - 1; + if (border_width > width) border_width = width - 1; + + auto px_data = (uint8_t*)image.GetData(); + auto a_data = (uint8_t*)image.GetAlpha(); + + for (size_t x = 0; x < width; ++x) { + for (size_t y = 0; y < height; ++y) { + if (x < border_width || y < border_width || + x >= (width - border_width) || y >= (height - border_width)) { + const size_t idx = (x + y * width); + const size_t idx_rgb = (x + y * width) * 3; + px_data[idx_rgb] = px_data[idx_rgb + 1] = px_data[idx_rgb + 2] = dark_mode ? 245u : 110u; + a_data[idx] = 255u; + } + } + } + } + + bitmaps.push_back(wxImage_to_wxBitmap_with_alpha(std::move(image), scale)); + } + return wxBitmapBundle::FromBitmaps(bitmaps); +} + +wxBitmapBundle* BitmapCache::mksolid_bndl(size_t width, size_t height, const std::string& color, size_t border_width, bool dark_mode) +{ + std::string bitmap_key = (color.empty() ? "empty-w" : color) + "-h" + std::to_string(height) + "-w" + std::to_string(width) + (dark_mode ? "-dm" : ""); + + wxBitmapBundle* bndl = nullptr; + auto it = m_bndl_map.find(bitmap_key); + if (it == m_bndl_map.end()) { + if (color.empty()) + bndl = new wxBitmapBundle(mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, size_t(0))); + else { + ColorRGB rgb;// [3] ; + decode_color(color, rgb); + bndl = new wxBitmapBundle(mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, border_width, dark_mode)); + } + m_bndl_map[bitmap_key] = bndl; + } + else + return it->second; + + return bndl; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 5af90c5f7..28058d94b 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -24,10 +24,18 @@ public: void clear(); double scale() { return m_scale; } + wxBitmapBundle* find_bndl(const std::string &name) { auto it = m_bndl_map.find(name); return (it == m_bndl_map.end()) ? nullptr : it->second; } + const wxBitmapBundle* find_bndl(const std::string &name) const { return const_cast(this)->find_bndl(name); } wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; } const wxBitmap* find(const std::string &name) const { return const_cast(this)->find(name); } - wxBitmap* insert(const std::string &name, size_t width, size_t height); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const char* data, size_t width, size_t height); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const wxBitmapBundle &bmp); + wxBitmapBundle* insert_bndl(const std::string& bitmap_key, const wxVector& bmps); + wxBitmapBundle* insert_bndl(const std::string& name, const std::vector& bmps); + wxBitmapBundle* insert_raw_rgba_bndl(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); + + wxBitmap* insert(const std::string &name, size_t width, size_t height, double scale = -1.0); wxBitmap* insert(const std::string &name, const wxBitmap &bmp); wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); @@ -42,15 +50,24 @@ public: // And makes replases befor parsing // replace_map containes old_value->new_value static NSVGimage* nsvgParseFromFileWithReplace(const char* filename, const char* units, float dpi, const std::map& replaces); + // Gets a data from SVG file and makes replases + // replace_map containes old_value->new_value + static void nsvgGetDataFromFileWithReplace(const char* filename, std::string& data_str, const std::map& replaces); + wxBitmapBundle* from_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height, const bool dark_mode, const std::string& new_color = ""); + wxBitmapBundle* from_png(const std::string& bitmap_name, unsigned width, unsigned height); // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = ""); wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false); wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } - wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } + wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, true, 0); } + wxBitmapBundle mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width = 0, bool dark_mode = false); + wxBitmapBundle* mksolid_bndl(size_t width, size_t height, const std::string& color = std::string(), size_t border_width = 0, bool dark_mode = false); + wxBitmapBundle* mkclear_bndl(size_t width, size_t height) { return mksolid_bndl(width, height); } private: std::map m_map; + std::map m_bndl_map; double m_gs = 0.2; // value, used for image.ConvertToGreyscale(m_gs, m_gs, m_gs) double m_scale = 1.0; // value, used for correct scaling of SVG icons on Retina display }; diff --git a/src/slic3r/GUI/BitmapComboBox.cpp b/src/slic3r/GUI/BitmapComboBox.cpp index 54e1a31fa..70c985cf9 100644 --- a/src/slic3r/GUI/BitmapComboBox.cpp +++ b/src/slic3r/GUI/BitmapComboBox.cpp @@ -54,17 +54,6 @@ using Slic3r::GUI::format_wxstr; namespace Slic3r { namespace GUI { -/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ - BitmapComboBox::BitmapComboBox(wxWindow* parent, wxWindowID id/* = wxID_ANY*/, const wxString& value/* = wxEmptyString*/, @@ -90,72 +79,6 @@ BitmapComboBox::~BitmapComboBox() { } -#ifdef __APPLE__ -bool BitmapComboBox::OnAddBitmap(const wxBitmap& bitmap) -{ - if (bitmap.IsOk()) - { - // we should use scaled! size values of bitmap - int width = (int)bitmap.GetScaledWidth(); - int height = (int)bitmap.GetScaledHeight(); - - if (m_usedImgSize.x < 0) - { - // If size not yet determined, get it from this image. - m_usedImgSize.x = width; - m_usedImgSize.y = height; - - // Adjust control size to vertically fit the bitmap - wxWindow* ctrl = GetControl(); - ctrl->InvalidateBestSize(); - wxSize newSz = ctrl->GetBestSize(); - wxSize sz = ctrl->GetSize(); - if (newSz.y > sz.y) - ctrl->SetSize(sz.x, newSz.y); - else - DetermineIndent(); - } - - wxCHECK_MSG(width == m_usedImgSize.x && height == m_usedImgSize.y, - false, - "you can only add images of same size"); - - return true; - } - - return false; -} - -void BitmapComboBox::OnDrawItem(wxDC& dc, - const wxRect& rect, - int item, - int flags) const -{ - const wxBitmap& bmp = *(static_cast(m_bitmaps[item])); - if (bmp.IsOk()) - { - // we should use scaled! size values of bitmap - wxCoord w = bmp.GetScaledWidth(); - wxCoord h = bmp.GetScaledHeight(); - - const int imgSpacingLeft = 4; - - // Draw the image centered - dc.DrawBitmap(bmp, - rect.x + (m_usedImgSize.x - w) / 2 + imgSpacingLeft, - rect.y + (rect.height - h) / 2, - true); - } - - wxString text = GetString(item); - if (!text.empty()) - dc.DrawText(text, - rect.x + m_imgAreaWidth + 1, - rect.y + (rect.height - dc.GetCharHeight()) / 2); -} -#endif - - #ifdef _WIN32 int BitmapComboBox::Append(const wxString& item) @@ -166,19 +89,11 @@ int BitmapComboBox::Append(const wxString& item) //2. But then set width to 0 value for no using of bitmap left and right spacing //3. Set this empty bitmap to the at list one item and BitmapCombobox will be recreated correct -// wxBitmap bitmap(1, int(1.6 * wxGetApp().em_unit() + 1)); - wxBitmap bitmap(1, 16); - { - // bitmap.SetWidth(0); is depricated now - // so, use next code - bitmap.UnShare();// AllocExclusive(); - bitmap.GetGDIImageData()->m_width = 0; - } - + wxBitmapBundle bitmap = *get_empty_bmp_bundle(1, 16); OnAddBitmap(bitmap); + const int n = wxComboBox::Append(item); - if (n != wxNOT_FOUND) - DoSetItemBitmap(n, bitmap); + return n; } @@ -269,7 +184,6 @@ void BitmapComboBox::DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED( void BitmapComboBox::Rescale() { - return; // Next workaround: To correct scaling of a BitmapCombobox // we need to refill control with new bitmaps const wxString selection = this->GetValue(); diff --git a/src/slic3r/GUI/BitmapComboBox.hpp b/src/slic3r/GUI/BitmapComboBox.hpp index a77bf401d..545213fc3 100644 --- a/src/slic3r/GUI/BitmapComboBox.hpp +++ b/src/slic3r/GUI/BitmapComboBox.hpp @@ -29,28 +29,13 @@ BitmapComboBox(wxWindow* parent, #ifdef _WIN32 int Append(const wxString& item); #endif - int Append(const wxString& item, const wxBitmap& bitmap) + int Append(const wxString& item, const wxBitmapBundle& bitmap) { return wxBitmapComboBox::Append(item, bitmap); } protected: -#ifdef __APPLE__ -/* For PresetComboBox we use bitmaps that are created from images that are already scaled appropriately for Retina - * (Contrary to the intuition, the `scale` argument for Bitmap's constructor doesn't mean - * "please scale this to such and such" but rather - * "the wxImage is already sized for backing scale such and such". ) - * Unfortunately, the constructor changes the size of wxBitmap too. - * Thus We need to use unscaled size value for bitmaps that we use - * to avoid scaled size of control items. - * For this purpose control drawing methods and - * control size calculation methods (virtual) are overridden. - **/ -bool OnAddBitmap(const wxBitmap& bitmap) override; -void OnDrawItem(wxDC& dc, const wxRect& rect, int item, int flags) const override; -#endif - #ifdef _WIN32 bool MSWOnDraw(WXDRAWITEMSTRUCT* item) override; void DrawBackground_(wxDC& dc, const wxRect& rect, int WXUNUSED(item), int flags) const; diff --git a/src/slic3r/GUI/ButtonsDescription.cpp b/src/slic3r/GUI/ButtonsDescription.cpp index 2c5262d47..37daffd9d 100644 --- a/src/slic3r/GUI/ButtonsDescription.cpp +++ b/src/slic3r/GUI/ButtonsDescription.cpp @@ -17,9 +17,6 @@ void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWi wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(3, 5, 5); sizer->Add(grid_sizer, 0, wxEXPAND); - ScalableBitmap bmp_delete = ScalableBitmap(parent, "cross"); - ScalableBitmap bmp_delete_focus = ScalableBitmap(parent, "cross_focus"); - auto add_color = [grid_sizer, parent](wxColourPickerCtrl** color_picker, const wxColour& color, const wxColour& def_color, wxString label_text) { // wrap the label_text to the max 80 characters if (label_text.Len() > 80) { diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 080de997e..b5da80e90 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1609,7 +1609,7 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) #ifndef __WXOSX__ SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX #endif //__WXOSX__ - SetMinSize(bg.bmp().GetSize()); + SetMinSize(bg.GetSize()); const wxSize size = GetTextExtent("m"); em_w = size.x; @@ -1734,8 +1734,8 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) wxPaintDC dc(this); - const auto bullet_w = bullet_black.bmp().GetSize().GetWidth(); - const auto bullet_h = bullet_black.bmp().GetSize().GetHeight(); + const auto bullet_w = bullet_black.GetWidth(); + const auto bullet_h = bullet_black.GetHeight(); const int yoff_icon = bullet_h < em_h ? (em_h - bullet_h) / 2 : 0; const int yoff_text = bullet_h > em_h ? (bullet_h - em_h) / 2 : 0; const int yinc = item_height(); @@ -1748,10 +1748,10 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) unsigned x = em_w/2 + item.indent * em_w; if (i == item_active || (item_hover >= 0 && i == (size_t)item_hover)) { - dc.DrawBitmap(bullet_blue.bmp(), x, y + yoff_icon, false); + dc.DrawBitmap(bullet_blue.get_bitmap(), x, y + yoff_icon, false); } - else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); } - else if (i > item_active) { dc.DrawBitmap(bullet_white.bmp(), x, y + yoff_icon, false); } + else if (i < item_active) { dc.DrawBitmap(bullet_black.get_bitmap(), x, y + yoff_icon, false); } + else if (i > item_active) { dc.DrawBitmap(bullet_white.get_bitmap(), x, y + yoff_icon, false); } x += + bullet_w + em_w/2; const auto text_size = dc.GetTextExtent(item.label); @@ -1763,9 +1763,9 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt) } //draw logo - if (int y = size.y - bg.GetBmpHeight(); y>=0) { - dc.DrawBitmap(bg.bmp(), 0, y, false); - index_width = std::max(index_width, bg.GetBmpWidth() + em_w / 2); + if (int y = size.y - bg.GetHeight(); y>=0) { + dc.DrawBitmap(bg.get_bitmap(), 0, y, false); + index_width = std::max(index_width, bg.GetWidth() + em_w / 2); } if (GetMinSize().x < index_width) { @@ -1797,12 +1797,8 @@ void ConfigWizardIndex::msw_rescale() em_w = size.x; em_h = size.y; - bg.msw_rescale(); - SetMinSize(bg.bmp().GetSize()); + SetMinSize(bg.GetSize()); - bullet_black.msw_rescale(); - bullet_blue.msw_rescale(); - bullet_white.msw_rescale(); Refresh(); } diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 4de8381ff..aa074f925 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -519,7 +519,7 @@ private: ssize_t item_hover; size_t last_page; - int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; } + int item_height() const { return std::max(bullet_black.GetHeight(), em_w) + em_w; } void on_paint(wxPaintEvent &evt); void on_mouse_move(wxMouseEvent &evt); diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index dda50ec05..717af39ba 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -86,24 +86,24 @@ Control::Control( wxWindow *parent, m_bmp_thumb_higher = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_right") : ScalableBitmap(this, "thumb_up")); m_bmp_thumb_lower = (style == wxSL_HORIZONTAL ? ScalableBitmap(this, "thumb_left") : ScalableBitmap(this, "thumb_down")); - m_thumb_size = m_bmp_thumb_lower.GetBmpSize(); + m_thumb_size = m_bmp_thumb_lower.GetSize(); m_bmp_add_tick_on = ScalableBitmap(this, "colorchange_add"); m_bmp_add_tick_off = ScalableBitmap(this, "colorchange_add_f"); m_bmp_del_tick_on = ScalableBitmap(this, "colorchange_del"); m_bmp_del_tick_off = ScalableBitmap(this, "colorchange_del_f"); - m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth(); + m_tick_icon_dim = m_bmp_add_tick_on.GetWidth(); m_bmp_one_layer_lock_on = ScalableBitmap(this, "lock_closed"); m_bmp_one_layer_lock_off = ScalableBitmap(this, "lock_closed_f"); m_bmp_one_layer_unlock_on = ScalableBitmap(this, "lock_open"); m_bmp_one_layer_unlock_off = ScalableBitmap(this, "lock_open_f"); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.GetWidth(); m_bmp_revert = ScalableBitmap(this, "undo"); - m_revert_icon_dim = m_bmp_revert.GetBmpWidth(); + m_revert_icon_dim = m_bmp_revert.GetWidth(); m_bmp_cog = ScalableBitmap(this, "cog"); - m_cog_icon_dim = m_bmp_cog.GetBmpWidth(); + m_cog_icon_dim = m_bmp_cog.GetWidth(); m_selection = ssUndef; m_ticks.set_pause_print_msg(_utf8(L("Place bearings in slots and resume printing"))); @@ -155,26 +155,11 @@ void Control::msw_rescale() { m_font = GUI::wxGetApp().normal_font(); - m_bmp_thumb_higher.msw_rescale(); - m_bmp_thumb_lower .msw_rescale(); - m_thumb_size = m_bmp_thumb_lower.bmp().GetSize(); - - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.bmp().GetSize().x; - - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x; - - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x; - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.bmp().GetSize().x; + m_thumb_size = m_bmp_thumb_lower.GetSize(); + m_tick_icon_dim = m_bmp_add_tick_on.GetWidth(); + m_lock_icon_dim = m_bmp_one_layer_lock_on.GetWidth(); + m_revert_icon_dim = m_bmp_revert.GetWidth(); + m_cog_icon_dim = m_bmp_cog.GetWidth(); SLIDER_MARGIN = 4 + GUI::wxGetApp().em_unit(); @@ -189,22 +174,18 @@ void Control::sys_color_changed() { GUI::wxGetApp().UpdateDarkUI(GetParent()); - m_bmp_add_tick_on .msw_rescale(); - m_bmp_add_tick_off.msw_rescale(); - m_bmp_del_tick_on .msw_rescale(); - m_bmp_del_tick_off.msw_rescale(); - m_tick_icon_dim = m_bmp_add_tick_on.GetBmpWidth(); + m_bmp_add_tick_on .sys_color_changed(); + m_bmp_add_tick_off.sys_color_changed(); + m_bmp_del_tick_on .sys_color_changed(); + m_bmp_del_tick_off.sys_color_changed(); - m_bmp_one_layer_lock_on .msw_rescale(); - m_bmp_one_layer_lock_off .msw_rescale(); - m_bmp_one_layer_unlock_on .msw_rescale(); - m_bmp_one_layer_unlock_off.msw_rescale(); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetBmpWidth(); + m_bmp_one_layer_lock_on .sys_color_changed(); + m_bmp_one_layer_lock_off .sys_color_changed(); + m_bmp_one_layer_unlock_on .sys_color_changed(); + m_bmp_one_layer_unlock_off.sys_color_changed(); - m_bmp_revert.msw_rescale(); - m_revert_icon_dim = m_bmp_revert.GetBmpWidth(); - m_bmp_cog.msw_rescale(); - m_cog_icon_dim = m_bmp_cog.GetBmpWidth(); + m_bmp_revert.sys_color_changed(); + m_bmp_cog .sys_color_changed(); } int Control::GetActiveValue() const @@ -604,9 +585,12 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ return; } - wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + //wxBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off.bmp() : &m_bmp_add_tick_on.bmp(); + //if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) + // icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + ScalableBitmap* icon = m_focus == fiActionIcon ? &m_bmp_add_tick_off : &m_bmp_add_tick_on; if (m_ticks.ticks.find(TickCode{tick}) != m_ticks.ticks.end()) - icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off.bmp() : &m_bmp_del_tick_on.bmp(); + icon = m_focus == fiActionIcon ? &m_bmp_del_tick_off : &m_bmp_del_tick_on; wxCoord x_draw, y_draw; is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; @@ -615,10 +599,12 @@ void Control::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_ else is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - if (m_draw_mode == dmSequentialFffPrint) - dc.DrawBitmap(create_scaled_bitmap("colorchange_add", nullptr, 16, true), x_draw, y_draw); + if (m_draw_mode == dmSequentialFffPrint) { + wxBitmap disabled_add = get_bmp_bundle("colorchange_add")->GetBitmapFor(this).ConvertToDisabled(); + dc.DrawBitmap(disabled_add, x_draw, y_draw); + } else - dc.DrawBitmap(*icon, x_draw, y_draw); + dc.DrawBitmap((*icon).get_bitmap(), x_draw, y_draw); //update rect of the tick action icon m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); @@ -851,7 +837,7 @@ void Control::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider { wxCoord x_draw = pos.x - int(0.5 * m_thumb_size.x); wxCoord y_draw = pos.y - int(0.5 * m_thumb_size.y); - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.bmp() : m_bmp_thumb_higher.bmp(), x_draw, y_draw); + dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower.get_bitmap() : m_bmp_thumb_higher.get_bitmap(), x_draw, y_draw); // Update thumb rect update_thumb_rect(x_draw, y_draw, selection); @@ -945,12 +931,12 @@ void Control::draw_ticks(wxDC& dc) // Draw icon for "Pause print", "Custom Gcode" or conflict tick if (!icon_name.empty()) { - wxBitmap icon = create_scaled_bitmap(icon_name); + wxBitmapBundle* icon = get_bmp_bundle(icon_name); wxCoord x_draw, y_draw; is_horizontal() ? x_draw = pos - 0.5 * m_tick_icon_dim : y_draw = pos - 0.5 * m_tick_icon_dim; is_horizontal() ? y_draw = mid + 22 : x_draw = mid + m_thumb_size.x + 3; - dc.DrawBitmap(icon, x_draw, y_draw); + dc.DrawBitmap(icon->GetBitmapFor(this), x_draw, y_draw); } } } @@ -1262,9 +1248,12 @@ void Control::draw_one_layer_icon(wxDC& dc) if (m_draw_mode == dmSequentialGCodeView) return; - const wxBitmap& icon = m_is_one_layer ? - m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : - m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + //const wxBitmap& icon = m_is_one_layer ? + // m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off.bmp() : m_bmp_one_layer_lock_on.bmp() : + // m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off.bmp() : m_bmp_one_layer_unlock_on.bmp(); + const ScalableBitmap& icon = m_is_one_layer ? + m_focus == fiOneLayerIcon ? m_bmp_one_layer_lock_off : m_bmp_one_layer_lock_on : + m_focus == fiOneLayerIcon ? m_bmp_one_layer_unlock_off : m_bmp_one_layer_unlock_on; int width, height; get_size(&width, &height); @@ -1273,7 +1262,7 @@ void Control::draw_one_layer_icon(wxDC& dc) is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - dc.DrawBitmap(icon, x_draw, y_draw); + dc.DrawBitmap(icon.bmp().GetBitmapFor(this), x_draw, y_draw); //update rect of the lock/unlock icon m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); @@ -1291,7 +1280,7 @@ void Control::draw_revert_icon(wxDC& dc) is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN; is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2; - dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw); + dc.DrawBitmap(m_bmp_revert.get_bitmap(), x_draw, y_draw); //update rect of the lock/unlock icon m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim); @@ -1315,7 +1304,7 @@ void Control::draw_cog_icon(wxDC& dc) is_horizontal() ? y_draw = height - m_cog_icon_dim - 2 : y_draw = height - 2; } - dc.DrawBitmap(m_bmp_cog.bmp(), x_draw, y_draw); + dc.DrawBitmap(m_bmp_cog.get_bitmap(), x_draw, y_draw); //update rect of the lock/unlock icon m_rect_cog_icon = wxRect(x_draw, y_draw, m_cog_icon_dim, m_cog_icon_dim); @@ -1673,7 +1662,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current if (extruders_cnt > 1) { std::array active_extruders = get_active_extruders_for_tick(m_selection == ssLower ? m_lower_value : m_higher_value); - std::vector icons = get_extruder_color_icons(true); + std::vector icons = get_extruder_color_icons(true); wxMenu* change_extruder_menu = new wxMenu(); @@ -1684,7 +1673,7 @@ void Control::append_change_extruder_menu_item(wxMenu* menu, bool switch_current if (m_mode == MultiAsSingle) append_menu_item(change_extruder_menu, wxID_ANY, item_name, "", - [this, i](wxCommandEvent&) { add_code_as_tick(ToolChange, i); }, *icons[i-1], menu, + [this, i](wxCommandEvent&) { add_code_as_tick(ToolChange, i); }, icons[i-1], menu, [is_active_extruder]() { return !is_active_extruder; }, GUI::wxGetApp().plater()); } @@ -1722,7 +1711,7 @@ void Control::append_add_color_change_menu_item(wxMenu* menu, bool switch_curren format_wxstr(_L("Switch code to Color change (%1%) for:"), gcode(ColorChange)) : format_wxstr(_L("Add color change (%1%) for:"), gcode(ColorChange)); wxMenuItem* add_color_change_menu_item = menu->AppendSubMenu(add_color_change_menu, menu_name, ""); - add_color_change_menu_item->SetBitmap(create_menu_bitmap("colorchange_add_m")); + add_color_change_menu_item->SetBitmap(*get_bmp_bundle("colorchange_add_m")); } } diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index d72e1dd32..9bccb6b63 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -297,7 +297,7 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR if (can_create_editor_ctrl && !can_create_editor_ctrl()) return nullptr; - std::vector icons = get_extruder_color_icons(); + std::vector icons = get_extruder_color_icons(); if (icons.empty()) return nullptr; diff --git a/src/slic3r/GUI/ExtruderSequenceDialog.cpp b/src/slic3r/GUI/ExtruderSequenceDialog.cpp index 42313636e..e1c6a7ce0 100644 --- a/src/slic3r/GUI/ExtruderSequenceDialog.cpp +++ b/src/slic3r/GUI/ExtruderSequenceDialog.cpp @@ -264,9 +264,6 @@ void ExtruderSequenceDialog::on_dpi_changed(const wxRect& suggested_rect) { SetFont(wxGetApp().normal_font()); - m_bmp_add.msw_rescale(); - m_bmp_del.msw_rescale(); - const int em = em_unit(); m_intervals_grid_sizer->SetHGap(em); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index a9812abf2..95caa8ed3 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -106,14 +106,14 @@ public: bool set_undo_to_sys_tooltip(const wxString* tip) { return m_undo_ui.set_undo_to_sys_tooltip(tip); } // ui items used for revert line value - bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; } - const wxBitmap& undo_bitmap() const { return m_undo_ui.undo_bitmap->bmp(); } - const wxString* undo_tooltip() const { return m_undo_ui.undo_tooltip; } - const wxBitmap& undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap->bmp(); } - const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; } - const wxColour* label_color() const { return m_undo_ui.label_color; } - const bool blink() const { return m_undo_ui.blink; } - bool* get_blink_ptr() { return &m_undo_ui.blink; } + bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; } + const wxBitmapBundle& undo_bitmap() const { return m_undo_ui.undo_bitmap->bmp(); } + const wxString* undo_tooltip() const { return m_undo_ui.undo_tooltip; } + const wxBitmapBundle& undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap->bmp(); } + const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; } + const wxColour* label_color() const { return m_undo_ui.label_color; } + const bool blink() const { return m_undo_ui.blink; } + bool* get_blink_ptr() { return &m_undo_ui.blink; } }; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4b0544488..0412bca21 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -331,7 +331,7 @@ private: // See https://github.com/wxWidgets/wxWidgets/blob/master/src/msw/font.cpp // void wxNativeFontInfo::SetFractionalPointSize(float pointSizeNew) wxNativeFontInfo nfi= *font.GetNativeFontInfo(); - float pointSizeNew = scale * font.GetPointSize(); + float pointSizeNew = wxDisplay(this).GetScaleFactor() * scale * font.GetPointSize(); nfi.lf.lfHeight = nfi.GetLogFontHeightAtPPI(pointSizeNew, get_dpi_for_window(this)); nfi.pointSize = pointSizeNew; font = wxFont(nfi); @@ -1179,7 +1179,7 @@ bool GUI_App::on_init_inner() } // create splash screen with updated bmp - scrn = new SplashScreen(bmp.IsOk() ? bmp : create_scaled_bitmap("PrusaSlicer", nullptr, 400), + scrn = new SplashScreen(bmp.IsOk() ? bmp : get_bmp_bundle("PrusaSlicer", 400)->GetPreferredBitmapSizeAtScale(1.0), wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT, 4000, splashscreen_pos); if (!default_splashscreen_pos) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 4adb161c2..6a3dad5f4 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -142,19 +142,11 @@ std::map SettingsFactory::CATEGORY_ICON = { L("Hollowing") , "hollowing" } }; -//wxBitmap SettingsFactory::get_category_bitmap(const std::string& category_name, bool menu_bmp /*= true*/) -wxBitmap SettingsFactory::get_category_bitmap_(const std::string& category_name, bool menu_bmp /*= true*/) +wxBitmapBundle* SettingsFactory::get_category_bitmap(const std::string& category_name) { if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) - return wxNullBitmap; - return /*menu_bmp ? create_menu_bitmap(CATEGORY_ICON.at(category_name)) : */create_scaled_bitmap(CATEGORY_ICON.at(category_name)); -} - -wxBitmapBundle SettingsFactory::get_category_bitmap(const std::string& category_name) -{ - if (CATEGORY_ICON.find(category_name) == CATEGORY_ICON.end()) - return wxNullBitmap; - return create_menu_bitmap(CATEGORY_ICON.at(category_name)); + return get_bmp_bundle("empty"); + return get_bmp_bundle(CATEGORY_ICON.at(category_name)); } //------------------------------------- @@ -437,13 +429,12 @@ static void create_freq_settings_popupmenu(wxMenu* menu, const bool is_object_se #endif } -std::vector MenuFactory::get_volume_bitmaps() +std::vector MenuFactory::get_volume_bitmaps() { - std::vector volume_bmps; + std::vector volume_bmps; volume_bmps.reserve(ADD_VOLUME_MENU_ITEMS.size()); for (auto item : ADD_VOLUME_MENU_ITEMS) -// volume_bmps.push_back(create_menu_bitmap(item.second)); - volume_bmps.push_back(create_scaled_bitmap(item.second, nullptr, 16, false, "", true)); + volume_bmps.push_back(get_bmp_bundle(item.second)); return volume_bmps; } @@ -623,7 +614,7 @@ wxMenuItem* MenuFactory::append_menu_item_settings(wxMenu* menu_) // Add full settings list auto menu_item = new wxMenuItem(menu, wxID_ANY, menu_name); - menu_item->SetBitmap(create_menu_bitmap("cog")); + menu_item->SetBitmap(*get_bmp_bundle("cog")); menu_item->SetSubMenu(create_settings_popupmenu(menu, is_object_settings, item)); return menu->Append(menu_item); @@ -768,7 +759,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) return; } - std::vector icons = get_extruder_color_icons(true); + std::vector icons = get_extruder_color_icons(true); wxMenu* extruder_selection_menu = new wxMenu(); const wxString& name = sels.Count() == 1 ? names[0] : names[1]; @@ -787,7 +778,7 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu) (is_active_extruder ? " (" + _L("active") + ")" : ""); append_menu_item(extruder_selection_menu, wxID_ANY, item_name, "", - [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, *icons[icon_idx], menu, + [i](wxCommandEvent&) { obj_list()->set_extruder_for_selected_items(i); }, icons[icon_idx], menu, [is_active_extruder]() { return !is_active_extruder; }, m_parent); } @@ -1147,12 +1138,6 @@ void MenuFactory::update_default_menu() create_default_menu(); } -void MenuFactory::msw_rescale() -{ - for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) - msw_rescale_menu(dynamic_cast(menu)); -} - #ifdef _WIN32 // For this class is used code from stackoverflow: // https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence @@ -1182,7 +1167,7 @@ static void update_menu_item_def_colors(T* item) void MenuFactory::sys_color_changed() { for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu }) { - msw_rescale_menu(dynamic_cast(menu));// msw_rescale_menu updates just icons, so use it + sys_color_changed_menu(dynamic_cast(menu));// msw_rescale_menu updates just icons, so use it #ifdef _WIN32 // but under MSW we have to update item's bachground color for (wxMenuItem* item : menu->GetMenuItems()) @@ -1195,14 +1180,17 @@ void MenuFactory::sys_color_changed(wxMenuBar* menubar) { for (size_t id = 0; id < menubar->GetMenuCount(); id++) { wxMenu* menu = menubar->GetMenu(id); - msw_rescale_menu(menu); + sys_color_changed_menu(menu); +#ifndef __linux__ + menu->SetupBitmaps(); #ifdef _WIN32 // but under MSW we have to update item's bachground color for (wxMenuItem* item : menu->GetMenuItems()) update_menu_item_def_colors(item); +#endif #endif } - menubar->Refresh(); +// menubar->Refresh(); } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index cba828c29..bbbc00d42 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -25,9 +25,7 @@ struct SettingsFactory typedef std::map> Bundle; static std::map CATEGORY_ICON; -// static wxBitmap get_category_bitmap(const std::string& category_name, bool menu_bmp = true); - static wxBitmap get_category_bitmap_(const std::string& category_name, bool menu_bmp = true); - static wxBitmapBundle get_category_bitmap(const std::string& category_name); + static wxBitmapBundle* get_category_bitmap(const std::string& category_name); static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings); static std::vector get_options(bool is_part); }; @@ -36,7 +34,7 @@ class MenuFactory { public: static const std::vector> ADD_VOLUME_MENU_ITEMS; - static std::vector get_volume_bitmaps(); + static std::vector get_volume_bitmaps(); MenuFactory(); ~MenuFactory() = default; @@ -45,7 +43,6 @@ public: void update(); void update_object_menu(); void update_default_menu(); - void msw_rescale(); void sys_color_changed(); static void sys_color_changed(wxMenuBar* menu_bar); diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index b7ff8e48f..437a526af 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -234,47 +234,47 @@ void ObjectLayers::UpdateAndShow(const bool show) void ObjectLayers::msw_rescale() { - m_bmp_delete.msw_rescale(); - m_bmp_add.msw_rescale(); + //m_bmp_delete.msw_rescale(); + //m_bmp_add.msw_rescale(); - m_grid_sizer->SetHGap(wxGetApp().em_unit()); + //m_grid_sizer->SetHGap(wxGetApp().em_unit()); - // rescale edit-boxes - const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); - for (int i = 0; i < cells_cnt; ++i) { - const wxSizerItem* item = m_grid_sizer->GetItem(i); - if (item->IsWindow()) { - LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); - if (editor != nullptr) - editor->msw_rescale(); - } - else if (item->IsSizer()) // case when we have editor with buttons - { - wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor - if (e_item->IsWindow()) { - LayerRangeEditor* editor = dynamic_cast(e_item->GetWindow()); - if (editor != nullptr) - editor->msw_rescale(); - } + //// rescale edit-boxes + //const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); + //for (int i = 0; i < cells_cnt; ++i) { + // const wxSizerItem* item = m_grid_sizer->GetItem(i); + // if (item->IsWindow()) { + // LayerRangeEditor* editor = dynamic_cast(item->GetWindow()); + // if (editor != nullptr) + // editor->msw_rescale(); + // } + // else if (item->IsSizer()) // case when we have editor with buttons + // { + // wxSizerItem* e_item = item->GetSizer()->GetItem(size_t(0)); // editor + // if (e_item->IsWindow()) { + // LayerRangeEditor* editor = dynamic_cast(e_item->GetWindow()); + // if (editor != nullptr) + // editor->msw_rescale(); + // } - if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons - for (size_t btn : {2, 3}) { // del_btn, add_btn - wxSizerItem* b_item = item->GetSizer()->GetItem(btn); - if (b_item->IsWindow()) { - auto button = dynamic_cast(b_item->GetWindow()); - if (button != nullptr) - button->msw_rescale(); - } - } - } - } + // if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons + // for (size_t btn : {2, 3}) { // del_btn, add_btn + // wxSizerItem* b_item = item->GetSizer()->GetItem(btn); + // if (b_item->IsWindow()) { + // auto button = dynamic_cast(b_item->GetWindow()); + // if (button != nullptr) + // button->msw_rescale(); + // } + // } + // } + //} m_grid_sizer->Layout(); } void ObjectLayers::sys_color_changed() { - m_bmp_delete.msw_rescale(); - m_bmp_add.msw_rescale(); + m_bmp_delete.sys_color_changed(); + m_bmp_add.sys_color_changed(); // rescale edit-boxes const int cells_cnt = m_grid_sizer->GetCols() * m_grid_sizer->GetEffectiveRowsCount(); @@ -286,7 +286,7 @@ void ObjectLayers::sys_color_changed() if (b_item->IsWindow()) { auto button = dynamic_cast(b_item->GetWindow()); if (button != nullptr) - button->msw_rescale(); + button->sys_color_changed(); } } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index bfedd8e1e..1171149b6 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4239,9 +4239,6 @@ void ObjectList::msw_rescale() GetColumn(colExtruder)->SetWidth( 8 * em); GetColumn(colEditing )->SetWidth( 3 * em); - // rescale/update existing items with bitmaps - m_objects_model->Rescale(); - Layout(); } @@ -4249,7 +4246,10 @@ void ObjectList::sys_color_changed() { wxGetApp().UpdateDVCDarkUI(this, true); - msw_rescale(); + // update existing items with bitmaps + m_objects_model->UpdateBitmaps(); + + Layout(); } void ObjectList::ItemValueChanged(wxDataViewEvent &event) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 24ae01389..a538f2b33 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -126,7 +126,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Load bitmaps to be used for the mirroring buttons: m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on"); m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off"); - m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png"); + m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent"); const int border = wxOSX ? 0 : 4; const int em = wxGetApp().em_unit(); @@ -1009,7 +1009,7 @@ void ObjectManipulation::update_warning_icon_state(const MeshErrorsInfo& warning m_manifold_warning_bmp = ScalableBitmap(m_parent, warning_icon_name); const wxString& tooltip = warning.tooltip; m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); - m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize()); + m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.GetSize()); m_fix_throught_netfab_bitmap->SetToolTip(tooltip); } @@ -1336,25 +1336,10 @@ void ObjectManipulation::msw_rescale() m_item_name->SetMinSize(wxSize(20*em, wxDefaultCoord)); msw_rescale_word_local_combo(m_word_local_combo); m_word_local_combo_sizer->SetMinSize(wxSize(-1, m_word_local_combo->GetBestHeight(-1))); - m_manifold_warning_bmp.msw_rescale(); const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText(); m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); - m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.bmp().GetSize()); - - m_mirror_bitmap_on.msw_rescale(); - m_mirror_bitmap_off.msw_rescale(); - m_mirror_bitmap_hidden.msw_rescale(); - m_reset_scale_button->msw_rescale(); - m_reset_rotation_button->msw_rescale(); -#if ENABLE_WORLD_COORDINATE - m_reset_skew_button->msw_rescale(); -#endif /// ENABLE_WORLD_COORDINATE - m_drop_to_bed_button->msw_rescale(); - m_lock_bnt->msw_rescale(); - - for (int id = 0; id < 3; ++id) - m_mirror_buttons[id].first->msw_rescale(); + m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.GetSize()); // rescale label-heights // Text trick to grid sizer layout: @@ -1383,20 +1368,16 @@ void ObjectManipulation::sys_color_changed() for (ManipulationEditor* editor : m_editors) editor->sys_color_changed(this); - // btn...->msw_rescale() updates icon on button, so use it - m_mirror_bitmap_on.msw_rescale(); - m_mirror_bitmap_off.msw_rescale(); - m_mirror_bitmap_hidden.msw_rescale(); - m_reset_scale_button->msw_rescale(); - m_reset_rotation_button->msw_rescale(); -#if ENABLE_WORLD_COORDINATE - m_reset_skew_button->msw_rescale(); -#endif // ENABLE_WORLD_COORDINATE - m_drop_to_bed_button->msw_rescale(); - m_lock_bnt->msw_rescale(); + m_mirror_bitmap_on.sys_color_changed(); + m_mirror_bitmap_off.sys_color_changed(); + m_mirror_bitmap_hidden.sys_color_changed(); + m_reset_scale_button->sys_color_changed(); + m_reset_rotation_button->sys_color_changed(); + m_drop_to_bed_button->sys_color_changed(); + m_lock_bnt->sys_color_changed(); for (int id = 0; id < 3; ++id) - m_mirror_buttons[id].first->msw_rescale(); + m_mirror_buttons[id].first->sys_color_changed(); } #if ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 291013fe9..97eb5f10d 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -99,7 +99,7 @@ bool ObjectSettings::update_settings_list() btn->SetToolTip(_(L("Remove parameter"))); btn->SetBitmapFocus(m_bmp_delete_focus.bmp()); - btn->SetBitmapHover(m_bmp_delete_focus.bmp()); + btn->SetBitmapCurrent(m_bmp_delete_focus.bmp()); btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) { wxGetApp().plater()->take_snapshot(from_u8((boost::format(_utf8(L("Delete Option %s"))) % opt_key).str())); @@ -133,7 +133,7 @@ bool ObjectSettings::update_settings_list() return; ctrl->SetBitmap_(m_bmp_delete); ctrl->SetBitmapFocus(m_bmp_delete_focus.bmp()); - ctrl->SetBitmapHover(m_bmp_delete_focus.bmp()); + ctrl->SetBitmapCurrent(m_bmp_delete_focus.bmp()); }; const bool is_extruders_cat = cat.first == "Extruders"; @@ -268,15 +268,6 @@ void ObjectSettings::UpdateAndShow(const bool show) OG_Settings::UpdateAndShow(show ? update_settings_list() : false); } -void ObjectSettings::msw_rescale() -{ - m_bmp_delete.msw_rescale(); - m_bmp_delete_focus.msw_rescale(); - - for (auto group : m_og_settings) - group->msw_rescale(); -} - void ObjectSettings::sys_color_changed() { m_og->sys_color_changed(); diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index e5a6937f1..5d0be1308 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -56,7 +56,6 @@ public: bool add_missed_options(ModelConfig *config_to, const DynamicPrintConfig &config_from); void update_config_values(ModelConfig *config); void UpdateAndShow(const bool show) override; - void msw_rescale(); void sys_color_changed(); }; diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index 975b807dc..6be6a94fe 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -157,8 +157,9 @@ bool GalleryDialog::can_change_thumbnail() void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect) { - const int& em = em_unit(); + update(); + const int& em = em_unit(); msw_buttons_rescale(this, em, { ID_BTN_ADD_CUSTOM_SHAPE, ID_BTN_DEL_CUSTOM_SHAPE, ID_BTN_REPLACE_CUSTOM_PNG, wxID_OK, wxID_CLOSE }); wxSize size = wxSize(50 * em, 35 * em); @@ -169,13 +170,14 @@ void GalleryDialog::on_dpi_changed(const wxRect& suggested_rect) Refresh(); } -static void add_lock(wxImage& image) +static void add_lock(wxImage& image, wxWindow* parent_win) { - int lock_sz = 22; + wxBitmapBundle* bmp_bndl = get_bmp_bundle("lock", 22); #ifdef __APPLE__ - lock_sz /= mac_max_scaling_factor(); + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize() * mac_max_scaling_factor()); +#else + wxBitmap bmp = bmp_bndl->GetBitmapFor(parent_win); #endif - wxBitmap bmp = create_scaled_bitmap("lock", nullptr, lock_sz); wxImage lock_image = bmp.ConvertToImage(); if (!lock_image.IsOk() || lock_image.GetWidth() == 0 || lock_image.GetHeight() == 0) @@ -213,21 +215,28 @@ static void add_lock(wxImage& image) } } -static void add_default_image(wxImageList* img_list, bool is_system) +static void add_default_image(wxImageList* img_list, bool is_system, wxWindow* parent_win) { - int sz = IMG_PX_CNT; + wxBitmapBundle* bmp_bndl = get_bmp_bundle("cog", IMG_PX_CNT); #ifdef __APPLE__ - sz /= mac_max_scaling_factor(); + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize() * mac_max_scaling_factor()); +#else + wxBitmap bmp = bmp_bndl->GetBitmapFor(parent_win); #endif - wxBitmap bmp = create_scaled_bitmap("cog", nullptr, sz, true); + bmp = bmp.ConvertToDisabled(); if (is_system) { wxImage image = bmp.ConvertToImage(); if (image.IsOk() && image.GetWidth() != 0 && image.GetHeight() != 0) { - add_lock(image); + add_lock(image, parent_win); +#ifdef __APPLE__ + bmp = wxBitmap(std::move(image), -1, mac_max_scaling_factor()); +#else bmp = wxBitmap(std::move(image)); +#endif } } + img_list->Add(bmp); }; @@ -344,8 +353,13 @@ void GalleryDialog::load_label_icon_list() // Make an image list containing large icons +#ifdef __APPLE__ + m_image_list = new wxImageList(IMG_PX_CNT, IMG_PX_CNT); + int px_cnt = IMG_PX_CNT * mac_max_scaling_factor(); +#else int px_cnt = (int)(em_unit() * IMG_PX_CNT * 0.1f + 0.5f); m_image_list = new wxImageList(px_cnt, px_cnt); +#endif std::string ext = ".png"; @@ -364,7 +378,7 @@ void GalleryDialog::load_label_icon_list() if (can_generate_thumbnail) generate_thumbnail_from_model(model_name); else { - add_default_image(m_image_list, item.is_system); + add_default_image(m_image_list, item.is_system, this); continue; } } @@ -373,14 +387,18 @@ void GalleryDialog::load_label_icon_list() if (!image.CanRead(from_u8(img_name)) || !image.LoadFile(from_u8(img_name), wxBITMAP_TYPE_PNG) || image.GetWidth() == 0 || image.GetHeight() == 0) { - add_default_image(m_image_list, item.is_system); + add_default_image(m_image_list, item.is_system, this); continue; } image.Rescale(px_cnt, px_cnt, wxIMAGE_QUALITY_BILINEAR); if (item.is_system) - add_lock(image); + add_lock(image, this); +#ifdef __APPLE__ + wxBitmap bmp = wxBitmap(std::move(image), -1, mac_max_scaling_factor()); +#else wxBitmap bmp = wxBitmap(std::move(image)); +#endif m_image_list->Add(bmp); } diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index a749ad405..bf4fe9dc7 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -270,9 +270,7 @@ wxPanel* KBShortcutsDialog::create_header(wxWindow* parent, const wxFont& bold_f sizer->AddStretchSpacer(); // logo - //m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 32); - //m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, m_logo_bmp.bmp()); - m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, wxBitmapBundle::FromSVGFile(Slic3r::var(wxGetApp().logo_name() + ".svg"), wxSize(32, 32))); + m_header_bitmap = new wxStaticBitmap(panel, wxID_ANY, *get_bmp_bundle(wxGetApp().logo_name(), 32)); sizer->Add(m_header_bitmap, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 3d80954bd..bf1f67994 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1014,9 +1014,6 @@ void MainFrame::on_dpi_changed(const wxRect& suggested_rect) for (auto tab : wxGetApp().tabs_list) tab->msw_rescale(); - for (size_t id = 0; id < m_menubar->GetMenuCount(); id++) - msw_rescale_menu(m_menubar->GetMenu(id)); - // Workarounds for correct Window rendering after rescale /* Even if Window is maximized during moving, @@ -1051,7 +1048,7 @@ void MainFrame::on_sys_color_changed() #ifdef _MSW_DARK_MODE // update common mode sizer if (!wxGetApp().tabs_as_menu()) - dynamic_cast(m_tabpanel)->Rescale(); + dynamic_cast(m_tabpanel)->OnColorsChanged(); #endif #endif @@ -1608,9 +1605,9 @@ void MainFrame::update_menubar() m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _L("S&end G-code") : _L("S&end to print")) + dots + "\tCtrl+Shift+G"); m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _L("&Filament Settings Tab") : _L("Mate&rial Settings Tab")) + "\tCtrl+3"); - m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_menu_bitmap(is_fff ? "spool" : "resin")); + m_changeable_menu_items[miMaterialTab] ->SetBitmap(*get_bmp_bundle(is_fff ? "spool" : "resin")); - m_changeable_menu_items[miPrinterTab] ->SetBitmap(create_menu_bitmap(is_fff ? "printer" : "sla_printer")); + m_changeable_menu_items[miPrinterTab] ->SetBitmap(*get_bmp_bundle(is_fff ? "printer" : "sla_printer")); } #if 0 diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 76bcfdd4a..43e13841c 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -99,17 +99,10 @@ void MsgDialog::apply_style(long style) if (style & wxNO) add_button(wxID_NO, (style & wxNO_DEFAULT)); if (style & wxCANCEL) add_button(wxID_CANCEL, (style & wxCANCEL_DEFAULT)); -#if 0 - logo->SetBitmap( create_scaled_bitmap(style & wxICON_WARNING ? "exclamation" : - style & wxICON_INFORMATION ? "info" : - style & wxICON_QUESTION ? "question" : "PrusaSlicer", this, 64, style & wxICON_ERROR)); -#else std::string icon_name = style & wxICON_WARNING ? "exclamation" : style & wxICON_INFORMATION ? "info" : style & wxICON_QUESTION ? "question" : "PrusaSlicer"; - icon_name += ".svg"; - logo->SetBitmap(wxBitmapBundle::FromSVGFile(Slic3r::var(icon_name), wxSize(64, 64))); -#endif + logo->SetBitmap(*get_bmp_bundle(icon_name, 64)); } void MsgDialog::finalize() @@ -238,7 +231,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_ add_msg_content(this, content_sizer, msg, monospaced_font); // Use a small bitmap with monospaced font, as the error text will not be wrapped. - logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : /*1*/84)); + logo->SetBitmap(*get_bmp_bundle("PrusaSlicer_192px_grayscale.png", monospaced_font ? 48 : /*1*/84)); SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit())); diff --git a/src/slic3r/GUI/Notebook.cpp b/src/slic3r/GUI/Notebook.cpp index 9c5ccb834..380b402d5 100644 --- a/src/slic3r/GUI/Notebook.cpp +++ b/src/slic3r/GUI/Notebook.cpp @@ -95,10 +95,6 @@ void ButtonsListCtrl::UpdateMode() void ButtonsListCtrl::Rescale() { - m_mode_sizer->msw_rescale(); - for (ScalableButton* btn : m_pageButtons) - btn->msw_rescale(); - int em = em_unit(this); m_btn_margin = std::lround(0.3 * em); m_line_margin = std::lround(0.1 * em); @@ -108,6 +104,14 @@ void ButtonsListCtrl::Rescale() m_sizer->Layout(); } +void ButtonsListCtrl::OnColorsChanged() +{ + for (ScalableButton* btn : m_pageButtons) + btn->sys_color_changed(); + + m_sizer->Layout(); +} + void ButtonsListCtrl::SetSelection(int sel) { if (m_selection == sel) diff --git a/src/slic3r/GUI/Notebook.hpp b/src/slic3r/GUI/Notebook.hpp index af03a6a08..bd6c5d85a 100644 --- a/src/slic3r/GUI/Notebook.hpp +++ b/src/slic3r/GUI/Notebook.hpp @@ -21,6 +21,7 @@ public: void SetSelection(int sel); void UpdateMode(); void Rescale(); + void OnColorsChanged(); bool InsertPage(size_t n, const wxString& text, bool bSelect = false, const std::string& bmp_name = ""); void RemovePage(size_t n); bool SetPageImage(size_t n, const std::string& bmp_name) const; @@ -245,6 +246,11 @@ public: GetBtnsListCtrl()->Rescale(); } + void OnColorsChanged() + { + GetBtnsListCtrl()->OnColorsChanged(); + } + void OnNavigationKey(wxNavigationKeyEvent& event) { if (event.IsWindowChange()) { diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 400db751a..c202de5e2 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -19,12 +19,12 @@ static bool is_point_in_rect(const wxPoint& pt, const wxRect& rect) rect.GetTop() <= pt.y && pt.y <= rect.GetBottom(); } -static wxSize get_bitmap_size(const wxBitmap& bmp) +static wxSize get_bitmap_size(const wxBitmapBundle* bmp, wxWindow* parent) { #ifdef __APPLE__ - return bmp.GetScaledSize(); + return bmp->GetDefaultSize(); #else - return bmp.GetSize(); + return bmp->GetBitmapFor(parent).GetSize(); #endif } @@ -45,8 +45,8 @@ OG_CustomCtrl::OG_CustomCtrl( wxWindow* parent, m_v_gap = lround(1.0 * m_em_unit); m_h_gap = lround(0.2 * m_em_unit); - m_bmp_mode_sz = get_bitmap_size(create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12)); - m_bmp_blinking_sz = get_bitmap_size(create_scaled_bitmap("search_blink", this)); + m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this); + m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("search_blink"), this); init_ctrl_lines();// from og.lines() @@ -416,8 +416,8 @@ void OG_CustomCtrl::msw_rescale() m_v_gap = lround(1.0 * m_em_unit); m_h_gap = lround(0.2 * m_em_unit); - m_bmp_mode_sz = create_scaled_bitmap("mode_simple", this, wxOSX ? 10 : 12).GetSize(); - m_bmp_blinking_sz = create_scaled_bitmap("search_blink", this).GetSize(); + m_bmp_mode_sz = get_bitmap_size(get_bmp_bundle("mode_simple", wxOSX ? 10 : 12), this); + m_bmp_blinking_sz = get_bitmap_size(get_bmp_bundle("search_blink"), this); m_max_win_width = 0; @@ -497,7 +497,7 @@ void OG_CustomCtrl::CtrlLine::msw_rescale() { // if we have a single option with no label, no sidetext if (draw_just_act_buttons) - height = get_bitmap_size(create_scaled_bitmap("empty")).GetHeight(); + height = get_bitmap_size(get_bmp_bundle("empty"), ctrl).GetHeight(); if (ctrl->opt_group->label_width != 0 && !og_line.label.IsEmpty()) { wxSize label_sz = ctrl->GetTextExtent(og_line.label); @@ -666,13 +666,13 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_mode_bmp(wxDC& dc, wxCoord v_pos) ConfigOptionMode mode = og_line.get_options()[0].opt.mode; const std::string& bmp_name = mode == ConfigOptionMode::comSimple ? "mode_simple" : mode == ConfigOptionMode::comAdvanced ? "mode_advanced" : "mode_expert"; - wxBitmap bmp = create_scaled_bitmap(bmp_name, ctrl, wxOSX ? 10 : 12); - wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp).GetHeight()) / 2); + wxBitmapBundle* bmp = get_bmp_bundle(bmp_name, wxOSX ? 10 : 12); + wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp, ctrl).GetHeight()) / 2); if (og_line.get_options().front().opt.gui_type != ConfigOptionDef::GUIType::legend) - dc.DrawBitmap(bmp, 0, y_draw); + dc.DrawBitmap(bmp->GetBitmapFor(ctrl), 0, y_draw); - return get_bitmap_size(bmp).GetWidth() + ctrl->m_h_gap; + return get_bitmap_size(bmp, ctrl).GetWidth() + ctrl->m_h_gap; } wxCoord OG_CustomCtrl::CtrlLine::draw_text(wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url/* = false*/) @@ -734,33 +734,33 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_text(wxDC& dc, wxPoint pos, const wxStr wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking) { - wxBitmap bmp_blinking = create_scaled_bitmap(is_blinking ? "search_blink" : "empty", ctrl); + wxBitmapBundle* bmp_blinking = get_bmp_bundle(is_blinking ? "search_blink" : "empty"); wxCoord h_pos = pos.x; - wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking).GetHeight()) / 2); + wxCoord v_pos = pos.y + lround((height - get_bitmap_size(bmp_blinking, ctrl).GetHeight()) / 2); - dc.DrawBitmap(bmp_blinking, h_pos, v_pos); + dc.DrawBitmap(bmp_blinking->GetBitmapFor(ctrl), h_pos, v_pos); - int bmp_dim = get_bitmap_size(bmp_blinking).GetWidth(); + int bmp_dim = get_bitmap_size(bmp_blinking, ctrl).GetWidth(); h_pos += bmp_dim + ctrl->m_h_gap; return wxPoint(h_pos, v_pos); } -wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id) +wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) { pos = draw_blinking_bmp(dc, pos, is_blinking); wxCoord h_pos = pos.x; wxCoord v_pos = pos.y; - dc.DrawBitmap(bmp_undo_to_sys, h_pos, v_pos); + dc.DrawBitmap(bmp_undo_to_sys.GetBitmapFor(ctrl), h_pos, v_pos); - int bmp_dim = get_bitmap_size(bmp_undo_to_sys).GetWidth(); + int bmp_dim = get_bitmap_size(&bmp_undo_to_sys, ctrl).GetWidth(); rects_undo_to_sys_icon[rect_id] = wxRect(h_pos, v_pos, bmp_dim, bmp_dim); h_pos += bmp_dim + ctrl->m_h_gap; - dc.DrawBitmap(bmp_undo, h_pos, v_pos); + dc.DrawBitmap(bmp_undo.GetBitmapFor(ctrl), h_pos, v_pos); - bmp_dim = get_bitmap_size(bmp_undo).GetWidth(); + bmp_dim = get_bitmap_size(&bmp_undo, ctrl).GetWidth(); rects_undo_icon[rect_id] = wxRect(h_pos, v_pos, bmp_dim, bmp_dim); h_pos += bmp_dim + ctrl->m_h_gap; diff --git a/src/slic3r/GUI/OG_CustomCtrl.hpp b/src/slic3r/GUI/OG_CustomCtrl.hpp index c15132fec..0308322f7 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.hpp +++ b/src/slic3r/GUI/OG_CustomCtrl.hpp @@ -63,7 +63,7 @@ class OG_CustomCtrl :public wxPanel wxCoord draw_mode_bmp(wxDC& dc, wxCoord v_pos); wxCoord draw_text (wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url = false); wxPoint draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking); - wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmap& bmp_undo_to_sys, const wxBitmap& bmp_undo, bool is_blinking, size_t rect_id = 0); + wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id = 0); bool launch_browser() const; bool is_separator() const { return og_line.is_separator(); } diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 4344deb24..05d0d60ec 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -56,7 +56,7 @@ const std::map INFO_ITEMS{ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmap& bmp, + const wxBitmapBundle& bmp, const wxString& extruder, const int idx/* = -1*/, const std::string& warning_icon_name /*= std::string*/) : @@ -101,7 +101,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent } else if (type == itLayerRoot) { - m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr + m_bmp = *get_bmp_bundle(LayerRootIcon); m_name = _(L("Layers")); } else if (type == itInfo) @@ -132,7 +132,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent } const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str(); m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")"; - m_bmp = create_scaled_bitmap(LayerIcon); // FIXME: pass window ptr + m_bmp = *get_bmp_bundle(LayerIcon); set_action_and_extruder_icons(); init_container(); @@ -151,7 +151,7 @@ void ObjectDataViewModelNode::set_action_and_extruder_icons() { m_action_icon_name = m_type & itObject ? "advanced_plus" : m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj"; - m_action_icon = create_scaled_bitmap(m_action_icon_name); // FIXME: pass window ptr + m_action_icon = *get_bmp_bundle(m_action_icon_name); // set extruder bitmap set_extruder_icon(); @@ -170,7 +170,7 @@ void ObjectDataViewModelNode::set_printable_icon(PrintIndicator printable) { m_printable = printable; m_printable_icon = m_printable == piUndef ? m_empty_bmp : - create_scaled_bitmap(m_printable == piPrintable ? "eye_open.png" : "eye_closed.png"); + *get_bmp_bundle(m_printable == piPrintable ? "eye_open" : "eye_closed"); } void ObjectDataViewModelNode::set_warning_icon(const std::string& warning_icon_name) @@ -185,14 +185,14 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps() m_bmp = m_empty_bmp; std::string scaled_bitmap_name = m_name.ToUTF8().data(); - scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : ""); + scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : ""); - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); if (bmp == nullptr) { - std::vector bmps; + std::vector bmps; for (auto& category : m_opt_categories) - bmps.emplace_back(SettingsFactory::get_category_bitmap_(category, false)); - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + bmps.emplace_back(SettingsFactory::get_category_bitmap(category)); + bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps); } m_bmp = *bmp; @@ -216,13 +216,13 @@ bool ObjectDataViewModelNode::update_settings_digest(const std::vectorGetExtruder().IsEmpty()) @@ -1676,14 +1676,14 @@ wxDataViewItem ObjectDataViewModel::SetObjectPrintableState( return obj_item; } -void ObjectDataViewModel::Rescale() +void ObjectDataViewModel::UpdateBitmaps() { m_volume_bmps = MenuFactory::get_volume_bitmaps(); - m_warning_bmp = create_scaled_bitmap(WarningIcon); - m_warning_manifold_bmp = create_scaled_bitmap(WarningManifoldIcon); + m_warning_bmp = *get_bmp_bundle(WarningIcon); + m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon); for (auto item : INFO_ITEMS) - m_info_bmps[item.first] = create_scaled_bitmap(item.second.bmp_name); + m_info_bmps[item.first] = get_bmp_bundle(item.second.bmp_name); wxDataViewItemArray all_items; GetAllChildren(wxDataViewItem(0), all_items); @@ -1694,7 +1694,7 @@ void ObjectDataViewModel::Rescale() continue; ObjectDataViewModelNode *node = static_cast(item.GetID()); - node->msw_rescale(); + node->sys_color_changed(); switch (node->m_type) { @@ -1705,11 +1705,11 @@ void ObjectDataViewModel::Rescale() node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_warning_icon_name); break; case itLayerRoot: - node->m_bmp = create_scaled_bitmap(LayerRootIcon); + node->m_bmp = *get_bmp_bundle(LayerRootIcon); case itLayer: - node->m_bmp = create_scaled_bitmap(LayerIcon); + node->m_bmp = *get_bmp_bundle(LayerIcon); case itInfo: - node->m_bmp = m_info_bmps.at(node->m_info_item_type); + node->m_bmp = *m_info_bmps.at(node->m_info_item_type); break; default: break; } @@ -1718,22 +1718,22 @@ void ObjectDataViewModel::Rescale() } } -wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/) +wxBitmapBundle ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/) { if (warning_icon_name.empty()) - return m_volume_bmps[static_cast(vol_type)]; + return *m_volume_bmps[static_cast(vol_type)]; std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast(vol_type)); scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm"); - wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name); + wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); if (bmp == nullptr) { - std::vector bmps; + std::vector bmps; - bmps.emplace_back(GetWarningBitmap(warning_icon_name)); + bmps.emplace_back(&GetWarningBitmap(warning_icon_name)); bmps.emplace_back(m_volume_bmps[static_cast(vol_type)]); - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps); } return *bmp; @@ -1768,7 +1768,7 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo return; if (node->GetType() & itVolume) { - node->SetWarningBitmap(m_volume_bmps[static_cast(node->volume_type())], ""); + node->SetWarningBitmap(*m_volume_bmps[static_cast(node->volume_type())], ""); return; } diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index f8885b206..7014acccb 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -63,21 +63,21 @@ class ObjectDataViewModelNode { ObjectDataViewModelNode* m_parent; MyObjectTreeModelNodePtrArray m_children; - wxBitmap m_empty_bmp; + wxBitmapBundle m_empty_bmp; size_t m_volumes_cnt = 0; std::vector< std::string > m_opt_categories; t_layer_height_range m_layer_range = { 0.0f, 0.0f }; wxString m_name; - wxBitmap& m_bmp = m_empty_bmp; + wxBitmapBundle& m_bmp = m_empty_bmp; ItemType m_type; int m_idx = -1; bool m_container = false; wxString m_extruder = "default"; - wxBitmap m_extruder_bmp; - wxBitmap m_action_icon; + wxBitmapBundle m_extruder_bmp; + wxBitmapBundle m_action_icon; PrintIndicator m_printable {piUndef}; - wxBitmap m_printable_icon; + wxBitmapBundle m_printable_icon; std::string m_warning_icon_name{ "" }; std::string m_action_icon_name = ""; @@ -99,7 +99,7 @@ public: ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmap& bmp, + const wxBitmapBundle& bmp, const wxString& extruder, const int idx = -1, const std::string& warning_icon_name = std::string()); @@ -179,10 +179,10 @@ public: bool SetValue(const wxVariant &variant, unsigned int col); void SetVolumeType(ModelVolumeType type) { m_volume_type = type; } - void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } + void SetBitmap(const wxBitmapBundle &icon) { m_bmp = icon; } void SetExtruder(const wxString &extruder) { m_extruder = extruder; } - void SetWarningBitmap(const wxBitmap& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; } - const wxBitmap& GetBitmap() const { return m_bmp; } + void SetWarningBitmap(const wxBitmapBundle& icon, const std::string& warning_icon_name) { m_bmp = icon; m_warning_icon_name = warning_icon_name; } + const wxBitmapBundle& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } ItemType GetType() const { return m_type; } InfoItemType GetInfoItemType() const { return m_info_item_type; } @@ -234,7 +234,7 @@ public: void update_settings_digest_bitmaps(); bool update_settings_digest(const std::vector& categories); int volume_type() const { return int(m_volume_type); } - void msw_rescale(); + void sys_color_changed(); #ifndef NDEBUG bool valid(); @@ -257,11 +257,11 @@ wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); class ObjectDataViewModel :public wxDataViewModel { std::vector m_objects; - std::vector m_volume_bmps; - std::map m_info_bmps; - wxBitmap m_empty_bmp; - wxBitmap m_warning_bmp; - wxBitmap m_warning_manifold_bmp; + std::vector m_volume_bmps; + std::map m_info_bmps; + wxBitmapBundle m_empty_bmp; + wxBitmapBundle m_warning_bmp; + wxBitmapBundle m_warning_manifold_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -315,7 +315,7 @@ public: // helper method for wxLog wxString GetName(const wxDataViewItem &item) const; - wxBitmap& GetBitmap(const wxDataViewItem &item) const; + wxBitmapBundle& GetBitmap(const wxDataViewItem &item) const; wxString GetExtruder(const wxDataViewItem &item) const; int GetExtruderNumber(const wxDataViewItem &item) const; @@ -383,9 +383,9 @@ public: void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } // Rescale bitmaps for existing Items - void Rescale(); + void UpdateBitmaps(); - wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, + wxBitmapBundle GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name = std::string()); void AddWarningIcon(const wxDataViewItem& item, const std::string& warning_name); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); @@ -403,7 +403,7 @@ private: wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); void AddAllChildren(const wxDataViewItem& parent); - wxBitmap& GetWarningBitmap(const std::string& warning_icon_name); + wxBitmapBundle& GetWarningBitmap(const std::string& warning_icon_name); }; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 257fe2532..6055a8e78 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -728,7 +728,6 @@ void ConfigOptionsGroup::msw_rescale() // check if window is ScalableButton ScalableButton* sc_btn = dynamic_cast(win); if (sc_btn) { - sc_btn->msw_rescale(); sc_btn->SetSize(sc_btn->GetBestSize()); return; } @@ -773,7 +772,7 @@ void ConfigOptionsGroup::sys_color_changed() wxWindow* win = item->GetWindow(); // check if window is ScalableButton if (ScalableButton* sc_btn = dynamic_cast(win)) { - sc_btn->msw_rescale(); + sc_btn->sys_color_changed(); return; } wxGetApp().UpdateDarkUI(win, dynamic_cast(win) != nullptr); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index b2983f97f..04ee9d090 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -142,10 +142,10 @@ void PresetForPrinter::AllowDelete() m_presets_list->update(); } -void PresetForPrinter::msw_rescale() +void PresetForPrinter::on_sys_color_changed() { - m_presets_list->msw_rescale(); - m_delete_preset_btn->msw_rescale(); + m_presets_list->sys_color_changed(); + m_delete_preset_btn->sys_color_changed(); } @@ -603,19 +603,10 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) { const int& em = em_unit(); - m_add_preset_btn->msw_rescale(); - m_printhost_browse_btn->msw_rescale(); - m_printhost_test_btn->msw_rescale(); - if (m_printhost_cafile_browse_btn) - m_printhost_cafile_browse_btn->msw_rescale(); - m_optgroup->msw_rescale(); msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); - for (PresetForPrinter* preset : m_presets) - preset->msw_rescale(); - const wxSize& size = wxSize(45 * em, 35 * em); SetMinSize(size); @@ -623,6 +614,18 @@ void PhysicalPrinterDialog::on_dpi_changed(const wxRect& suggested_rect) Refresh(); } +void PhysicalPrinterDialog::on_sys_color_changed() +{ + m_add_preset_btn->sys_color_changed(); + m_printhost_browse_btn->sys_color_changed(); + m_printhost_test_btn->sys_color_changed(); + if (m_printhost_cafile_browse_btn) + m_printhost_cafile_browse_btn->sys_color_changed(); + + for (PresetForPrinter* preset : m_presets) + preset->on_sys_color_changed(); +} + void PhysicalPrinterDialog::OnOK(wxEvent& event) { wxString printer_name = m_printer_name->GetValue(); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp index cb9a48b3e..d8bb70d3c 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.hpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp @@ -48,8 +48,7 @@ public: void SuppressDelete(); void AllowDelete(); - void msw_rescale(); - void on_sys_color_changed() {}; + void on_sys_color_changed(); }; @@ -98,7 +97,7 @@ public: void DeletePreset(PresetForPrinter* preset_for_printer); protected: void on_dpi_changed(const wxRect& suggested_rect) override; - void on_sys_color_changed() override {}; + void on_sys_color_changed() override; bool had_all_mk3; }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b78a6db95..c0a4bedf1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -180,7 +180,6 @@ public: bool showing_manifold_warning_icon; void show_sizer(bool show); - void msw_rescale(); void update_warning_icon(const std::string& warning_icon_name); }; @@ -210,7 +209,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : init_info_label(&info_size, _L("Size")); - info_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap("info")); + info_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle("info")); info_icon->SetToolTip(_L("For a multipart object, this value isn't accurate.\n" "It doesn't take account of intersections and negative volumes.")); auto* volume_info_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -223,7 +222,7 @@ ObjectInfo::ObjectInfo(wxWindow *parent) : info_manifold = new wxStaticText(parent, wxID_ANY, ""); info_manifold->SetFont(wxGetApp().small_font()); - manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name)); + manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, *get_bmp_bundle(m_warning_icon_name)); auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL); sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2); sizer_manifold->Add(info_manifold, 0, wxLEFT, 2); @@ -242,17 +241,11 @@ void ObjectInfo::show_sizer(bool show) manifold_warning_icon->Show(showing_manifold_warning_icon && show); } -void ObjectInfo::msw_rescale() -{ - manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); - info_icon->SetBitmap(create_scaled_bitmap("info")); -} - void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) { if ((showing_manifold_warning_icon = !warning_icon_name.empty())) { m_warning_icon_name = warning_icon_name; - manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); + manifold_warning_icon->SetBitmap(*get_bmp_bundle(m_warning_icon_name)); } } @@ -350,9 +343,6 @@ void FreqChangedParams::msw_rescale() { m_og->msw_rescale(); m_og_sla->msw_rescale(); - - for (auto btn: m_empty_buttons) - btn->msw_rescale(); } void FreqChangedParams::sys_color_changed() @@ -361,7 +351,7 @@ void FreqChangedParams::sys_color_changed() m_og_sla->sys_color_changed(); for (auto btn: m_empty_buttons) - btn->msw_rescale(); + btn->sys_color_changed(); wxGetApp().UpdateDarkUI(m_wiping_dialog_button, true); } @@ -450,7 +440,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : */ auto empty_widget = [this] (wxWindow* parent) { auto sizer = new wxBoxSizer(wxHORIZONTAL); - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString, + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); m_empty_buttons.push_back(btn); @@ -508,7 +498,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : } })); - auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString, + auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); @@ -1103,9 +1093,6 @@ void Sidebar::msw_rescale() { SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1)); - if (p->mode_sizer) - p->mode_sizer->msw_rescale(); - for (PlaterPresetComboBox* combo : std::vector { p->combo_print, p->combo_sla_print, p->combo_sla_material, @@ -1117,14 +1104,8 @@ void Sidebar::msw_rescale() p->frequently_changed_parameters->msw_rescale(); p->object_list->msw_rescale(); p->object_manipulation->msw_rescale(); - p->object_settings->msw_rescale(); p->object_layers->msw_rescale(); - p->object_info->msw_rescale(); - - p->btn_send_gcode->msw_rescale(); -// p->btn_eject_device->msw_rescale(); - p->btn_export_gcode_removable->msw_rescale(); #ifdef _WIN32 const int scaled_height = p->btn_export_gcode_removable->GetBitmapHeight(); #else @@ -1145,14 +1126,13 @@ void Sidebar::sys_color_changed() for (wxWindow* win : std::vector{ this, p->sliced_info->GetStaticBox(), p->object_info->GetStaticBox(), p->btn_reslice, p->btn_export_gcode }) wxGetApp().UpdateDarkUI(win); - p->object_info->msw_rescale(); for (wxWindow* win : std::vector{ p->scrolled, p->presets_panel }) wxGetApp().UpdateAllStaticTextDarkUI(win); for (wxWindow* btn : std::vector{ p->btn_reslice, p->btn_export_gcode }) wxGetApp().UpdateDarkUI(btn, true); if (p->mode_sizer) - p->mode_sizer->msw_rescale(); + p->mode_sizer->sys_color_changed(); p->frequently_changed_parameters->sys_color_changed(); p->object_settings->sys_color_changed(); #endif @@ -1170,11 +1150,12 @@ void Sidebar::sys_color_changed() p->object_layers->sys_color_changed(); // btn...->msw_rescale() updates icon on button, so use it - p->btn_send_gcode->msw_rescale(); + p->btn_send_gcode->sys_color_changed(); // p->btn_eject_device->msw_rescale(); - p->btn_export_gcode_removable->msw_rescale(); + p->btn_export_gcode_removable->sys_color_changed(); p->scrolled->Layout(); + p->scrolled->Refresh(); p->searcher.dlg_sys_color_changed(); } @@ -6971,8 +6952,6 @@ void Plater::msw_rescale() p->sidebar->msw_rescale(); - p->menus.msw_rescale(); - Layout(); GetParent()->Layout(); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index def48a1e4..93a5fe433 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -108,8 +108,8 @@ PresetComboBox::PresetComboBox(wxWindow* parent, Preset::Type preset_type, const default: break; } - m_bitmapCompatible = ScalableBitmap(this, "flag_green"); - m_bitmapIncompatible = ScalableBitmap(this, "flag_red"); + m_bitmapCompatible = get_bmp_bundle("flag_green"); + m_bitmapIncompatible = get_bmp_bundle("flag_red"); // parameters for an icon's drawing fill_width_height(); @@ -242,12 +242,12 @@ void PresetComboBox::update(std::string select_preset_name) const std::deque& presets = m_collection->get_presets(); - std::map> nonsys_presets; - std::map incomp_presets; + std::map> nonsys_presets; + std::map incomp_presets; wxString selected = ""; if (!presets.front().is_visible) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { @@ -268,7 +268,7 @@ void PresetComboBox::update(std::string select_preset_name) } std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + auto bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); if (!is_enabled) @@ -280,17 +280,17 @@ void PresetComboBox::update(std::string select_preset_name) } else { - nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); + nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); if (preset.name == select_preset_name || (select_preset_name.empty() && is_enabled)) selected = get_preset_name(preset); } if (i + 1 == m_collection->num_default_presets()) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); } if (!nonsys_presets.empty()) { - set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + set_label_marker(Append(separator(L("User presets")), NullBitmapBndl())); + for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { int item_id = Append(it->first, *it->second.first); bool is_enabled = it->second.second; if (!is_enabled) @@ -300,8 +300,8 @@ void PresetComboBox::update(std::string select_preset_name) } if (!incomp_presets.empty()) { - set_label_marker(Append(separator(L("Incompatible presets")), wxNullBitmap)); - for (std::map::iterator it = incomp_presets.begin(); it != incomp_presets.end(); ++it) { + set_label_marker(Append(separator(L("Incompatible presets")), NullBitmapBndl())); + for (std::map::iterator it = incomp_presets.begin(); it != incomp_presets.end(); ++it) { set_label_marker(Append(it->first, *it->second), LABEL_ITEM_DISABLED); } } @@ -337,7 +337,6 @@ bool PresetComboBox::del_physical_printer(const wxString& note_string/* = wxEmpt msg += note_string + "\n"; msg += format_wxstr(_L("Are you sure you want to delete \"%1%\" printer?"), printer_name); - //if (wxMessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES) if (MessageDialog(this, msg, _L("Delete Physical Printer"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal() != wxID_YES) return false; @@ -364,7 +363,8 @@ void PresetComboBox::show_all(bool show_all) void PresetComboBox::update() { - this->update(into_u8(this->GetString(this->GetSelection()))); + int n = this->GetSelection(); + this->update(n < 0 ? "" : into_u8(this->GetString(n))); } void PresetComboBox::update_from_bundle() @@ -375,43 +375,31 @@ void PresetComboBox::update_from_bundle() void PresetComboBox::msw_rescale() { m_em_unit = em_unit(this); +} - //m_bitmapIncompatible.msw_rescale(); - //m_bitmapCompatible.msw_rescale(); - - // parameters for an icon's drawing - fill_width_height(); +void PresetComboBox::sys_color_changed() +{ + m_bitmapCompatible = get_bmp_bundle("flag_green"); + m_bitmapIncompatible = get_bmp_bundle("flag_red"); + wxGetApp().UpdateDarkUI(this); // update the control to redraw the icons update(); } -void PresetComboBox::sys_color_changed() -{ - wxGetApp().UpdateDarkUI(this); - msw_rescale(); -} - void PresetComboBox::fill_width_height() { - // To avoid asserts, each added bitmap to wxBitmapCombobox should be the same size, so - // set a bitmap's height to m_bitmapCompatible->GetHeight() and norm_icon_width to m_bitmapCompatible->GetWidth() - icon_height = m_bitmapCompatible.GetBmpHeight(); - norm_icon_width = m_bitmapCompatible.GetBmpWidth(); + icon_height = m_bitmapCompatible->GetPreferredBitmapSizeAtScale(1.0).GetHeight(); + norm_icon_width = m_bitmapCompatible->GetPreferredBitmapSizeAtScale(1.0).GetWidth(); - /* It's supposed that standard size of an icon is 16px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ -// const float scale_f = (float)m_em_unit * 0.1f; - const float scale_f = 1.0f; + null_icon_width = 2 * norm_icon_width; - thin_icon_width = lroundf(8 * scale_f); // analogue to 8px; + thin_icon_width = 8; wide_icon_width = norm_icon_width + thin_icon_width; - space_icon_width = lroundf(2 * scale_f); - thin_space_icon_width = lroundf(4 * scale_f); - wide_space_icon_width = lroundf(6 * scale_f); + space_icon_width = 2; + thin_space_icon_width = 4; + wide_space_icon_width = 6; } wxString PresetComboBox::separator(const std::string& label) @@ -419,7 +407,8 @@ wxString PresetComboBox::separator(const std::string& label) return wxString::FromUTF8(separator_head()) + _(label) + wxString::FromUTF8(separator_tail()); } -wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, + +wxBitmapBundle* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, bool is_compatible/* = true*/, bool is_system/* = false*/, bool is_single_bar/* = false*/, const std::string& filament_rgb/* = ""*/, const std::string& extruder_rgb/* = ""*/, const std::string& material_rgb/* = ""*/) { @@ -435,45 +424,41 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, bool wide_icons, con bitmap_key += ",dark"; bitmap_key += material_rgb; - wxBitmap* bmp = bitmap_cache().find(bitmap_key); - if (bmp == nullptr) { + wxBitmapBundle* bmp_bndl = bitmap_cache().find_bndl(bitmap_key); + if (bmp_bndl == nullptr) { // Create the bitmap with color bars. - std::vector bmps; + std::vector bmps; if (wide_icons) // Paint a red flag for incompatible presets. - bmps.emplace_back(is_compatible ? bitmap_cache().mkclear(norm_icon_width, icon_height) : m_bitmapIncompatible.bmp()); + bmps.emplace_back(is_compatible ? get_empty_bmp_bundle(norm_icon_width, icon_height) : m_bitmapIncompatible); if (m_type == Preset::TYPE_FILAMENT && !filament_rgb.empty()) { // Paint the color bars. - ColorRGB color; - decode_color(filament_rgb, color); - bmps.emplace_back(bitmap_cache().mksolid(is_single_bar ? wide_icon_width : norm_icon_width, icon_height, color, false, 1, dark_mode)); - if (!is_single_bar) { - decode_color(extruder_rgb, color); - bmps.emplace_back(bitmap_cache().mksolid(thin_icon_width, icon_height, color, false, 1, dark_mode)); - } + bmps.emplace_back(get_solid_bmp_bundle(is_single_bar ? wide_icon_width : norm_icon_width, icon_height, filament_rgb)); + if (!is_single_bar) + bmps.emplace_back(get_solid_bmp_bundle(thin_icon_width, icon_height, extruder_rgb)); // Paint a lock at the system presets. - bmps.emplace_back(bitmap_cache().mkclear(space_icon_width, icon_height)); + bmps.emplace_back(get_empty_bmp_bundle(space_icon_width, icon_height)); } else { // Paint the color bars. - bmps.emplace_back(bitmap_cache().mkclear(thin_space_icon_width, icon_height)); + bmps.emplace_back(get_empty_bmp_bundle(thin_space_icon_width, icon_height)); if (m_type == Preset::TYPE_SLA_MATERIAL) - bmps.emplace_back(create_scaled_bitmap(main_icon_name, this, 16, false, material_rgb)); + bmps.emplace_back(bitmap_cache().from_svg(main_icon_name, 16, 16, dark_mode, material_rgb)); else - bmps.emplace_back(create_scaled_bitmap(main_icon_name)); + bmps.emplace_back(get_bmp_bundle(main_icon_name)); // Paint a lock at the system presets. - bmps.emplace_back(bitmap_cache().mkclear(wide_space_icon_width, icon_height)); + bmps.emplace_back(get_empty_bmp_bundle(wide_space_icon_width, icon_height)); } - bmps.emplace_back(is_system ? create_scaled_bitmap("lock_closed") : bitmap_cache().mkclear(norm_icon_width, icon_height)); - bmp = bitmap_cache().insert(bitmap_key, bmps); + bmps.emplace_back(is_system ? get_bmp_bundle("lock_closed") : get_empty_bmp_bundle(norm_icon_width, icon_height)); + bmp_bndl = bitmap_cache().insert_bndl(bitmap_key, bmps); } - return bmp; + return bmp_bndl; } -wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, +wxBitmapBundle* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, bool is_enabled/* = true*/, bool is_compatible/* = true*/, bool is_system/* = false*/) { bitmap_key += !is_enabled ? "_disabled" : ""; @@ -483,20 +468,25 @@ wxBitmap* PresetComboBox::get_bmp( std::string bitmap_key, const std::string& m if (wxGetApp().dark_mode()) bitmap_key += ",dark"; - wxBitmap* bmp = bitmap_cache().find(bitmap_key); + wxBitmapBundle* bmp = bitmap_cache().find_bndl(bitmap_key); if (bmp == nullptr) { // Create the bitmap with color bars. - std::vector bmps; - bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? create_scaled_bitmap(main_icon_name, this, 16, !is_enabled) : - is_compatible ? m_bitmapCompatible.bmp() : m_bitmapIncompatible.bmp()); + std::vector bmps; + bmps.emplace_back(m_type == Preset::TYPE_PRINTER ? get_bmp_bundle(main_icon_name) : + is_compatible ? m_bitmapCompatible : m_bitmapIncompatible); // Paint a lock at the system presets. - bmps.emplace_back(is_system ? create_scaled_bitmap(next_icon_name, this, 16, !is_enabled) : bitmap_cache().mkclear(norm_icon_width, icon_height)); - bmp = bitmap_cache().insert(bitmap_key, bmps); + bmps.emplace_back(is_system ? get_bmp_bundle(next_icon_name) : get_empty_bmp_bundle(norm_icon_width, icon_height)); + bmp = bitmap_cache().insert_bndl(bitmap_key, bmps); } return bmp; } +wxBitmapBundle PresetComboBox::NullBitmapBndl() +{ + return *get_empty_bmp_bundle(null_icon_width, icon_height); +} + bool PresetComboBox::is_selected_physical_printer() { auto selected_item = this->GetSelection(); @@ -783,14 +773,16 @@ void PlaterPresetComboBox::update() // and draw a red flag in front of the selected preset. bool wide_icons = selected_preset && !selected_preset->is_compatible; - std::map nonsys_presets; + null_icon_width = (wide_icons ? 3 : 2) * norm_icon_width + thin_space_icon_width + wide_space_icon_width; + + std::map nonsys_presets; wxString selected_user_preset; wxString tooltip; const std::deque& presets = m_collection->get_presets(); if (!presets.front().is_visible) - this->set_label_marker(this->Append(separator(L("System presets")), wxNullBitmap)); + this->set_label_marker(this->Append(separator(L("System presets")), NullBitmapBndl())); for (size_t i = presets.front().is_visible ? 0 : m_collection->num_default_presets(); i < presets.size(); ++i) { @@ -824,7 +816,7 @@ void PlaterPresetComboBox::update() material_rgb = print_config_def.get("material_colour")->get_default_value()->value; } - wxBitmap* bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, + auto bmp = get_bmp(bitmap_key, wide_icons, bitmap_type_name, preset.is_compatible, preset.is_system || preset.is_default, single_bar, filament_rgb, extruder_rgb, material_rgb); assert(bmp); @@ -845,12 +837,12 @@ void PlaterPresetComboBox::update() } } if (i + 1 == m_collection->num_default_presets()) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); } if (!nonsys_presets.empty()) { - set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + set_label_marker(Append(separator(L("User presets")), NullBitmapBndl())); + for (std::map::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { Append(it->first, *it->second); validate_selection(it->first == selected_user_preset); } @@ -860,7 +852,7 @@ void PlaterPresetComboBox::update() { // add Physical printers, if any exists if (!m_preset_bundle->physical_printers.empty()) { - set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + set_label_marker(Append(separator(L("Physical printers")), NullBitmapBndl())); const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { @@ -869,7 +861,7 @@ void PlaterPresetComboBox::update() if (!preset || !preset->is_visible) continue; std::string main_icon_name, bitmap_key = main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(main_icon_name, wide_icons, main_icon_name); + auto bmp = get_bmp(main_icon_name, wide_icons, main_icon_name); assert(bmp); set_label_marker(Append(from_u8(it->get_full_name(preset_name) + suffix(preset)), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); @@ -880,7 +872,7 @@ void PlaterPresetComboBox::update() } if (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL) { - wxBitmap* bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); + auto bmp = get_bmp("edit_preset_list", wide_icons, "edit_uni"); assert(bmp); if (m_type == Preset::TYPE_FILAMENT) @@ -917,9 +909,20 @@ void PlaterPresetComboBox::update() void PlaterPresetComboBox::msw_rescale() { PresetComboBox::msw_rescale(); - edit_btn->msw_rescale(); +#ifdef __WXMSW__ + // Use this part of code just on Windows to avoid of some layout issues on Linux + // see https://github.com/prusa3d/PrusaSlicer/issues/5163 and https://github.com/prusa3d/PrusaSlicer/issues/5505 + // Update control min size after rescale (changed Display DPI under MSW) + if (GetMinWidth() != 20 * m_em_unit) + SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight())); +#endif //__WXMSW__ } +void PlaterPresetComboBox::sys_color_changed() +{ + PresetComboBox::sys_color_changed(); + edit_btn->sys_color_changed(); +} // --------------------------------- // *** TabPresetComboBox *** @@ -982,10 +985,10 @@ void TabPresetComboBox::update() const std::deque& presets = m_collection->get_presets(); - std::map> nonsys_presets; + std::map> nonsys_presets; wxString selected = ""; if (!presets.front().is_visible) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); size_t idx_selected = m_collection->get_selected_idx(); if (m_type == Preset::TYPE_PRINTER && m_preset_bundle->physical_printers.has_selection()) { @@ -1012,7 +1015,7 @@ void TabPresetComboBox::update() } std::string main_icon_name = m_type == Preset::TYPE_PRINTER && preset.printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); + auto bmp = get_bmp(bitmap_key, main_icon_name, "lock_closed", is_enabled, preset.is_compatible, preset.is_system || preset.is_default); assert(bmp); if (preset.is_default || preset.is_system) { @@ -1023,18 +1026,18 @@ void TabPresetComboBox::update() } else { - std::pair pair(bmp, is_enabled); - nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); + std::pair pair(bmp, is_enabled); + nonsys_presets.emplace(get_preset_name(preset), std::pair(bmp, is_enabled)); if (i == idx_selected) selected = get_preset_name(preset); } if (i + 1 == m_collection->num_default_presets()) - set_label_marker(Append(separator(L("System presets")), wxNullBitmap)); + set_label_marker(Append(separator(L("System presets")), NullBitmapBndl())); } if (!nonsys_presets.empty()) { - set_label_marker(Append(separator(L("User presets")), wxNullBitmap)); - for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { + set_label_marker(Append(separator(L("User presets")), NullBitmapBndl())); + for (std::map>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { int item_id = Append(it->first, *it->second.first); bool is_enabled = it->second.second; if (!is_enabled) @@ -1047,7 +1050,7 @@ void TabPresetComboBox::update() { // add Physical printers, if any exists if (!m_preset_bundle->physical_printers.empty()) { - set_label_marker(Append(separator(L("Physical printers")), wxNullBitmap)); + set_label_marker(Append(separator(L("Physical printers")), NullBitmapBndl())); const PhysicalPrinterCollection& ph_printers = m_preset_bundle->physical_printers; for (PhysicalPrinterCollection::ConstIterator it = ph_printers.begin(); it != ph_printers.end(); ++it) { @@ -1057,7 +1060,7 @@ void TabPresetComboBox::update() continue; std::string main_icon_name = preset->printer_technology() == ptSLA ? "sla_printer" : m_main_bitmap_name; - wxBitmap* bmp = get_bmp(main_icon_name, main_icon_name, "", true, true, false); + auto bmp = get_bmp(main_icon_name, main_icon_name, "", true, true, false); assert(bmp); set_label_marker(Append(from_u8(it->get_full_name(preset_name) + suffix(preset)), *bmp), LABEL_ITEM_PHYSICAL_PRINTER); @@ -1068,7 +1071,7 @@ void TabPresetComboBox::update() // add "Add/Remove printers" item std::string icon_name = "edit_uni"; - wxBitmap* bmp = get_bmp("edit_preset_list, tab,", icon_name, ""); + auto bmp = get_bmp("edit_preset_list, tab,", icon_name, ""); assert(bmp); set_label_marker(Append(separator(L("Add/Remove printers")), *bmp), LABEL_ITEM_WIZARD_PRINTERS); diff --git a/src/slic3r/GUI/PresetComboBoxes.hpp b/src/slic3r/GUI/PresetComboBoxes.hpp index 40a0cfc28..9fe75c126 100644 --- a/src/slic3r/GUI/PresetComboBoxes.hpp +++ b/src/slic3r/GUI/PresetComboBoxes.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_PresetComboBoxes_hpp_ #define slic3r_PresetComboBoxes_hpp_ -//#include +#include #include #include "libslic3r/Preset.hpp" @@ -88,9 +88,9 @@ protected: static BitmapCache& bitmap_cache(); // Indicator, that the preset is compatible with the selected printer. - ScalableBitmap m_bitmapCompatible; + wxBitmapBundle* m_bitmapCompatible; // Indicator, that the preset is NOT compatible with the selected printer. - ScalableBitmap m_bitmapIncompatible; + wxBitmapBundle* m_bitmapIncompatible; int m_last_selected; int m_em_unit; @@ -99,6 +99,7 @@ protected: // parameters for an icon's drawing int icon_height; int norm_icon_width; + int null_icon_width; int thin_icon_width; int wide_icon_width; int space_icon_width; @@ -120,13 +121,15 @@ protected: #endif // __linux__ static wxString separator(const std::string& label); - wxBitmap* get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, + wxBitmapBundle* get_bmp( std::string bitmap_key, bool wide_icons, const std::string& main_icon_name, bool is_compatible = true, bool is_system = false, bool is_single_bar = false, const std::string& filament_rgb = "", const std::string& extruder_rgb = "", const std::string& material_rgb = ""); - wxBitmap* get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, + wxBitmapBundle* get_bmp( std::string bitmap_key, const std::string& main_icon_name, const std::string& next_icon_name, bool is_enabled = true, bool is_compatible = true, bool is_system = false); + wxBitmapBundle NullBitmapBndl(); + private: void fill_width_height(); }; @@ -155,6 +158,7 @@ public: wxString get_preset_name(const Preset& preset) override; void update() override; void msw_rescale() override; + void sys_color_changed() override; void OnSelect(wxCommandEvent& evt) override; private: diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 460b30126..2a2e3bb10 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -56,7 +56,7 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, from_u8((boost::format(_utf8(L("Save %s as:"))) % into_u8(tab->title())).str())); - m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, create_scaled_bitmap("tick_mark", m_parent)); + m_valid_bmp = new wxStaticBitmap(m_parent, wxID_ANY, *get_bmp_bundle("tick_mark")); m_combo = new wxComboBox(m_parent, wxID_ANY, from_u8(preset_name), wxDefaultPosition, wxSize(35 * wxGetApp().em_unit(), -1)); for (const std::string& value : values) @@ -173,7 +173,7 @@ void SavePresetDialog::Item::update_valid_bmp() { std::string bmp_name = m_valid_type == Warning ? "exclamation" : m_valid_type == NoValid ? "cross" : "tick_mark" ; - m_valid_bmp->SetBitmap(create_scaled_bitmap(bmp_name, m_parent)); + m_valid_bmp->SetBitmap(*get_bmp_bundle(bmp_name)); } void SavePresetDialog::Item::accept() diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 6b5edc30e..52e58193c 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -720,12 +720,9 @@ void SearchDialog::msw_rescale() { const int& em = em_unit(); - search_list_model->msw_rescale(); search_list->GetColumn(SearchListModel::colIcon )->SetWidth(3 * em); search_list->GetColumn(SearchListModel::colMarkedText)->SetWidth(45 * em); - msw_buttons_rescale(this, em, { wxID_CANCEL }); - const wxSize& size = wxSize(40 * em, 30 * em); SetMinSize(size); @@ -743,7 +740,7 @@ void SearchDialog::on_sys_color_changed() #endif // msw_rescale updates just icons, so use it - search_list_model->msw_rescale(); + search_list_model->sys_color_changed(); Refresh(); } @@ -776,10 +773,10 @@ void SearchListModel::Prepend(const std::string& label) RowPrepended(); } -void SearchListModel::msw_rescale() +void SearchListModel::sys_color_changed() { for (ScalableBitmap& bmp : m_icon) - bmp.msw_rescale(); + bmp.sys_color_changed(); } wxString SearchListModel::GetColumnType(unsigned int col) const @@ -795,7 +792,7 @@ void SearchListModel::GetValueByRow(wxVariant& variant, switch (col) { case colIcon: - variant << m_icon[m_values[row].second].bmp(); + variant << m_icon[m_values[row].second].bmp().GetBitmapFor(m_icon[m_values[row].second].parent()); break; case colMarkedText: variant = m_values[row].first; diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 49866d066..0d7851132 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -213,7 +213,7 @@ public: void Clear(); void Prepend(const std::string& text); - void msw_rescale(); + void sys_color_changed(); // implementation of base class virtuals to define model diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index db8cfc9c2..d4d83dfda 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -104,7 +104,7 @@ SysInfoDialog::SysInfoDialog() // logo //m_logo_bmp = ScalableBitmap(this, wxGetApp().logo_name(), 192); //m_logo = new wxStaticBitmap(this, wxID_ANY, m_logo_bmp.bmp()); - m_logo = new wxStaticBitmap(this, wxID_ANY, wxBitmapBundle::FromSVGFile(Slic3r::var(wxGetApp().logo_name() + ".svg"), wxSize(192, 192))); + m_logo = new wxStaticBitmap(this, wxID_ANY, *get_bmp_bundle(wxGetApp().logo_name(), 192)); hsizer->Add(m_logo, 0, wxALIGN_CENTER_VERTICAL); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1cb6ec8fc..d8d6dbf19 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -155,10 +155,8 @@ void Tab::create_preset_tab() add_scaled_button(panel, &m_btn_edit_ph_printer, "cog"); m_show_incompatible_presets = false; - add_scaled_bitmap(this, m_bmp_show_incompatible_presets, "flag_red"); - add_scaled_bitmap(this, m_bmp_hide_incompatible_presets, "flag_green"); - add_scaled_button(panel, &m_btn_hide_incompatible_presets, m_bmp_hide_incompatible_presets.name()); + add_scaled_button(panel, &m_btn_hide_incompatible_presets, "flag_green"); m_btn_compare_preset->SetToolTip(_L("Compare this preset with some another")); // TRN "Save current Settings" @@ -207,7 +205,7 @@ void Tab::create_preset_tab() #endif m_mode_sizer = new ModeSizer(panel, int (0.5*em_unit(this))); - const float scale_factor = /*wxGetApp().*/em_unit(this)*0.1;// GetContentScaleFactor(); + const float scale_factor = em_unit(this)*0.1;// GetContentScaleFactor(); m_hsizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3); m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); @@ -254,11 +252,8 @@ void Tab::create_preset_tab() m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); m_left_sizer->Add(m_treectrl, 1, wxEXPAND); - const int img_sz = int(16 * scale_factor + 0.5f); - m_icons = new wxImageList(img_sz, img_sz, true, 1); - // Index of the last icon inserted into $self->{icons}. + // Index of the last icon inserted into m_treectrl m_icon_count = -1; - m_treectrl->AssignImageList(m_icons); m_treectrl->AddRoot("root"); m_treectrl->SetIndent(0); wxGetApp().UpdateDarkUI(m_treectrl); @@ -321,6 +316,14 @@ void Tab::create_preset_tab() // Initialize the DynamicPrintConfig by default keys/values. build(); + if (!m_scaled_icons_list.empty()) { + // update icons for tree_ctrl + wxVector img_bundles; + for (const ScalableBitmap& bmp : m_scaled_icons_list) + img_bundles.push_back(bmp.bmp()); + m_treectrl->SetImages(img_bundles); + } + // ys_FIXME: Following should not be needed, the function will be called later // (update_mode->update_visibility->rebuild_page_tree). This does not work, during the // second call of rebuild_page_tree m_treectrl->GetFirstVisibleItem(); returns zero @@ -336,7 +339,7 @@ void Tab::add_scaled_button(wxWindow* parent, const wxString& label/* = wxEmptyString*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style, true); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, label, wxDefaultSize, wxDefaultPosition, style); m_scaled_buttons.push_back(*btn); } @@ -366,7 +369,6 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str if (icon_idx == -1) { // Add a new icon to the icon list. m_scaled_icons_list.push_back(ScalableBitmap(this, icon)); - m_icons->Add(m_scaled_icons_list.back().bmp()); icon_idx = ++m_icon_count; m_icon_index[icon] = icon_idx; } @@ -656,16 +658,6 @@ void TabPrinter::init_options_list() m_options_list.emplace("extruders_count", m_opt_status_value); } -void TabPrinter::msw_rescale() -{ - Tab::msw_rescale(); - - if (m_reset_to_filament_color) - m_reset_to_filament_color->msw_rescale(); - - Layout(); -} - void TabSLAMaterial::init_options_list() { if (!m_options_list.empty()) @@ -924,31 +916,9 @@ void Tab::msw_rescale() { m_em_unit = em_unit(m_parent); - if (m_mode_sizer) - m_mode_sizer->msw_rescale(); m_presets_choice->msw_rescale(); - m_treectrl->SetMinSize(wxSize(20 * m_em_unit, -1)); - // rescale buttons and cached bitmaps - for (const auto btn : m_scaled_buttons) - btn->msw_rescale(); - for (const auto bmp : m_scaled_bitmaps) - bmp->msw_rescale(); - - if (m_detach_preset_btn) - m_detach_preset_btn->msw_rescale(); - - // rescale icons for tree_ctrl - for (ScalableBitmap& bmp : m_scaled_icons_list) - bmp.msw_rescale(); - // recreate and set new ImageList for tree_ctrl - m_icons->RemoveAll(); - m_icons = new wxImageList(m_scaled_icons_list.front().bmp().GetWidth(), m_scaled_icons_list.front().bmp().GetHeight()); - for (ScalableBitmap& bmp : m_scaled_icons_list) - m_icons->Add(bmp.bmp()); - m_treectrl->AssignImageList(m_icons); - // rescale options_groups if (m_active_page) m_active_page->msw_rescale(); @@ -962,28 +932,28 @@ void Tab::sys_color_changed() // update buttons and cached bitmaps for (const auto btn : m_scaled_buttons) - btn->msw_rescale(); + btn->sys_color_changed(); for (const auto bmp : m_scaled_bitmaps) - bmp->msw_rescale(); + bmp->sys_color_changed(); if (m_detach_preset_btn) - m_detach_preset_btn->msw_rescale(); + m_detach_preset_btn->sys_color_changed(); + + update_show_hide_incompatible_button(); // update icons for tree_ctrl - for (ScalableBitmap& bmp : m_scaled_icons_list) - bmp.msw_rescale(); - // recreate and set new ImageList for tree_ctrl - m_icons->RemoveAll(); - m_icons = new wxImageList(m_scaled_icons_list.front().bmp().GetWidth(), m_scaled_icons_list.front().bmp().GetHeight()); - for (ScalableBitmap& bmp : m_scaled_icons_list) - m_icons->Add(bmp.bmp()); - m_treectrl->AssignImageList(m_icons); + wxVector img_bundles; + for (ScalableBitmap& bmp : m_scaled_icons_list) { + bmp.sys_color_changed(); + img_bundles.push_back(bmp.bmp()); + } + m_treectrl->SetImages(img_bundles); // Colors for ui "decoration" update_label_colours(); #ifdef _WIN32 wxWindowUpdateLocker noUpdates(this); if (m_mode_sizer) - m_mode_sizer->msw_rescale(); + m_mode_sizer->sys_color_changed(); wxGetApp().UpdateDarkUI(this); wxGetApp().UpdateDarkUI(m_treectrl); #endif @@ -994,6 +964,7 @@ void Tab::sys_color_changed() m_active_page->sys_color_changed(); Layout(); + Refresh(); } Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const @@ -1257,7 +1228,7 @@ void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup) auto detach_preset_btn = [this](wxWindow* parent) { m_detach_preset_btn = new ScalableButton(parent, wxID_ANY, "lock_open_sys", _L("Detach from system preset"), - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); ScalableButton* btn = m_detach_preset_btn; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); @@ -1670,14 +1641,14 @@ void TabPrint::build() option.opt.height = 5;//50; optgroup->append_single_option_line(option); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("notes"); option.opt.full_width = true; option.opt.height = 25;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) { @@ -1917,7 +1888,7 @@ void TabFilament::build() m_presets = &m_preset_bundle->filaments; load_initial_data(); - auto page = add_options_page(L("Filament"), "spool.png"); + auto page = add_options_page(L("Filament"), "spool"); auto optgroup = page->new_optgroup(L("Filament")); optgroup->append_single_option_line("filament_colour"); optgroup->append_single_option_line("filament_diameter"); @@ -2057,7 +2028,7 @@ void TabFilament::build() option.opt.height = gcode_field_height;// 150; optgroup->append_single_option_line(option); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); optgroup->label_width = 0; option = optgroup->get_option("filament_notes"); @@ -2065,7 +2036,7 @@ void TabFilament::build() option.opt.height = notes_field_height;// 250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) { return compatible_widget_create(parent, m_compatible_printers); @@ -2449,14 +2420,14 @@ void TabPrinter::build_fff() option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("printer_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); build_preset_description_line(optgroup.get()); @@ -2526,14 +2497,14 @@ void TabPrinter::build_sla() const int notes_field_height = 25; // 250 - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); option = optgroup->get_option("printer_notes"); option.opt.full_width = true; option.opt.height = notes_field_height;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); build_preset_description_line(optgroup.get()); @@ -2791,7 +2762,7 @@ void TabPrinter::build_unregular_pages(bool from_initial_build/* = false*/) auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) { m_reset_to_filament_color = new ScalableButton(parent, wxID_ANY, "undo", _L("Reset to Filament Color"), - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); ScalableButton* btn = m_reset_to_filament_color; btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); btn->SetSize(btn->GetBestSize()); @@ -3758,8 +3729,7 @@ void Tab::toggle_show_hide_incompatible() void Tab::update_show_hide_incompatible_button() { - m_btn_hide_incompatible_presets->SetBitmap_(m_show_incompatible_presets ? - m_bmp_show_incompatible_presets : m_bmp_hide_incompatible_presets); + m_btn_hide_incompatible_presets->SetBitmap(*get_bmp_bundle(m_show_incompatible_presets ? "flag_red" : "flag_green")); m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ? "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); @@ -3808,7 +3778,7 @@ wxSizer* Tab::compatible_widget_create(wxWindow* parent, PresetDependencies &dep deps.checkbox->SetFont(Slic3r::GUI::wxGetApp().normal_font()); wxGetApp().UpdateDarkUI(deps.checkbox, false, true); deps.btn = new ScalableButton(parent, wxID_ANY, "printer", from_u8((boost::format(" %s %s") % _utf8(L("Set")) % std::string(dots.ToUTF8())).str()), - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); deps.btn->SetFont(Slic3r::GUI::wxGetApp().normal_font()); deps.btn->SetSize(deps.btn->GetBestSize()); @@ -4117,7 +4087,7 @@ bool SubstitutionManager::is_empty_substitutions() wxSizer* TabPrint::create_manage_substitution_widget(wxWindow* parent) { auto create_btn = [parent](ScalableButton** btn, const wxString& label, const std::string& icon_name) { - *btn = new ScalableButton(parent, wxID_ANY, icon_name, " " + label + " ", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + *btn = new ScalableButton(parent, wxID_ANY, icon_name, " " + label + " ", wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); (*btn)->SetFont(wxGetApp().normal_font()); (*btn)->SetSize((*btn)->GetBestSize()); }; @@ -4170,7 +4140,7 @@ wxSizer* TabPrint::create_substitutions_widget(wxWindow* parent) wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) { ScalableButton* btn = new ScalableButton(parent, wxID_ANY, "printer", " " + _(L("Set")) + " " + dots, - wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT, true); + wxDefaultSize, wxDefaultPosition, wxBU_LEFT | wxBU_EXACTFIT); btn->SetFont(wxGetApp().normal_font()); btn->SetSize(btn->GetBestSize()); @@ -4564,7 +4534,7 @@ void TabSLAMaterial::build() optgroup->append_line(line); - page = add_options_page(L("Notes"), "note.png"); + page = add_options_page(L("Notes"), "note"); optgroup = page->new_optgroup(L("Notes"), 0); optgroup->label_width = 0; Option option = optgroup->get_option("material_notes"); @@ -4572,7 +4542,7 @@ void TabSLAMaterial::build() option.opt.height = 25;//250; optgroup->append_single_option_line(option); - page = add_options_page(L("Dependencies"), "wrench.png"); + page = add_options_page(L("Dependencies"), "wrench"); optgroup = page->new_optgroup(L("Profile dependencies")); create_line_with_widget(optgroup.get(), "compatible_printers", "", [this](wxWindow* parent) { @@ -4593,7 +4563,7 @@ void TabSLAMaterial::build() build_preset_description_line(optgroup.get()); - page = add_options_page(L("Material printing profile"), "note.png"); + page = add_options_page(L("Material printing profile"), "note"); optgroup = page->new_optgroup(L("Material printing profile")); option = optgroup->get_option("material_print_speed"); optgroup->append_single_option_line(option); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index d6f6e0112..6625f71f6 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -174,7 +174,6 @@ protected: wxBoxSizer* m_hsizer; wxBoxSizer* m_left_sizer; wxTreeCtrl* m_treectrl; - wxImageList* m_icons; wxScrolledWindow* m_page_view {nullptr}; wxBoxSizer* m_page_sizer {nullptr}; @@ -203,10 +202,6 @@ protected: ScalableButton* m_undo_to_sys_btn; ScalableButton* m_question_btn; - // Cached bitmaps. - // A "flag" icon to be displayned next to the preset name in the Tab's combo box. - ScalableBitmap m_bmp_show_incompatible_presets; - ScalableBitmap m_bmp_hide_incompatible_presets; // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. ScalableBitmap m_bmp_value_lock; ScalableBitmap m_bmp_value_unlock; @@ -505,7 +500,6 @@ public: void build_unregular_pages(bool from_initial_build = false); void on_preset_loaded() override; void init_options_list() override; - void msw_rescale() override; bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; } wxSizer* create_bed_shape_widget(wxWindow* parent); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index cab978284..ec5286abe 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -115,29 +115,21 @@ wxIcon ModelNode::get_bitmap(const wxString& color) wxBitmap ModelNode::get_bitmap(const wxString& color) #endif // __linux__ { - /* It's supposed that standard size of an icon is 48px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const double em = em_unit(m_parent_win); - const int icon_width = lround(6.4 * em); - const int icon_height = lround(1.6 * em); - - BitmapCache bmp_cache; - ColorRGB rgb; - decode_color(into_u8(color), rgb); - // there is no need to scale created solid bitmap + wxBitmap bmp = get_solid_bmp_bundle(64, 16, into_u8(color))->GetBitmapFor(m_parent_win); + if (!m_toggle) + bmp = bmp.ConvertToDisabled(); #ifndef __linux__ - return bmp_cache.mksolid(icon_width, icon_height, rgb, true); + return bmp; #else wxIcon icon; - icon.CopyFromBitmap(bmp_cache.mksolid(icon_width, icon_height, rgb, true)); + icon.CopyFromBitmap(bmp); return icon; #endif // __linux__ } // option node ModelNode::ModelNode(ModelNode* parent, const wxString& text, const wxString& old_value, const wxString& new_value) : + m_parent_win(parent->m_parent_win), m_parent(parent), m_old_color(old_value.StartsWith("#") ? old_value : ""), m_new_color(new_value.StartsWith("#") ? new_value : ""), @@ -202,18 +194,22 @@ void ModelNode::UpdateIcons() { // update icons for the colors, if any exists if (!m_old_color.IsEmpty()) - m_old_color_bmp = get_bitmap(m_toggle ? m_old_color : wxString::FromUTF8(grey.c_str())); + m_old_color_bmp = get_bitmap(m_old_color); if (!m_new_color.IsEmpty()) - m_new_color_bmp = get_bitmap(m_toggle ? m_new_color : wxString::FromUTF8(grey.c_str())); + m_new_color_bmp = get_bitmap(m_new_color); // update main icon, if any exists if (m_icon_name.empty()) return; + wxBitmap bmp = get_bmp_bundle(m_icon_name)->GetBitmapFor(m_parent_win); + if (!m_toggle) + bmp = bmp.ConvertToDisabled(); + #ifdef __linux__ - m_icon.CopyFromBitmap(create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle)); + m_icon.CopyFromBitmap(bmp); #else - m_icon = create_scaled_bitmap(m_icon_name, m_parent_win, 16, !m_toggle); + m_icon = bmp; #endif //__linux__ } @@ -838,7 +834,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ auto add_btn = [this, buttons, btn_font, dependent_presets](ScalableButton** btn, int& btn_id, const std::string& icon_name, Action close_act, const wxString& label, bool process_enable = true) { - *btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24); + *btn = new ScalableButton(this, btn_id = NewControlId(), icon_name, label, wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, 24); buttons->Add(*btn, 1, wxLEFT, 5); (*btn)->SetFont(btn_font); @@ -876,7 +872,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ if (ActionButtons::SAVE & m_buttons) add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save")); - ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, true, 24); + ScalableButton* cancel_btn = new ScalableButton(this, wxID_CANCEL, "cross", _L("Cancel"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, 24); buttons->Add(cancel_btn, 1, wxLEFT|wxRIGHT, 5); cancel_btn->SetFont(btn_font); cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); }); @@ -1307,8 +1303,6 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) int em = em_unit(); msw_buttons_rescale(this, em, { wxID_CANCEL, m_save_btn_id, m_move_btn_id, m_continue_btn_id }); - for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } ) - if (btn) btn->msw_rescale(); const wxSize& size = wxSize(70 * em, 30 * em); SetMinSize(size); @@ -1322,7 +1316,7 @@ void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) void UnsavedChangesDialog::on_sys_color_changed() { for (auto btn : { m_save_btn, m_transfer_btn, m_discard_btn } ) - btn->msw_rescale(); + btn->sys_color_changed(); // msw_rescale updates just icons, so use it m_tree->Rescale(); @@ -1720,10 +1714,16 @@ void DiffPresetDialog::on_dpi_changed(const wxRect&) const wxSize& size = wxSize(80 * em, 30 * em); SetMinSize(size); + auto rescale = [em](PresetComboBox* pcb) { + pcb->msw_rescale(); + wxSize sz = wxSize(35 * em, -1); + pcb->SetMinSize(sz); + pcb->SetSize(sz); + }; + for (auto preset_combos : m_preset_combos) { - preset_combos.presets_left->msw_rescale(); - preset_combos.equal_bmp->msw_rescale(); - preset_combos.presets_right->msw_rescale(); + rescale(preset_combos.presets_left); + rescale(preset_combos.presets_right); } m_tree->Rescale(em); @@ -1741,9 +1741,9 @@ void DiffPresetDialog::on_sys_color_changed() #endif for (auto preset_combos : m_preset_combos) { - preset_combos.presets_left->msw_rescale(); - preset_combos.equal_bmp->msw_rescale(); - preset_combos.presets_right->msw_rescale(); + preset_combos.presets_left->sys_color_changed(); + preset_combos.equal_bmp->sys_color_changed(); + preset_combos.presets_right->sys_color_changed(); } // msw_rescale updates just icons, so use it m_tree->Rescale(); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 9326c9258..67890bac8 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -24,18 +24,15 @@ #ifndef __linux__ // msw_menuitem_bitmaps is used for MSW and OSX static std::map msw_menuitem_bitmaps; -#ifdef __WXMSW__ -void msw_rescale_menu(wxMenu* menu) +void sys_color_changed_menu(wxMenu* menu) { - return; struct update_icons { static void run(wxMenuItem* item) { const auto it = msw_menuitem_bitmaps.find(item->GetId()); if (it != msw_menuitem_bitmaps.end()) { -// const wxBitmap& item_icon = create_menu_bitmap(it->second); - const wxBitmapBundle& item_icon = create_menu_bitmap(it->second); - if (item_icon.IsOk()) - item->SetBitmap(item_icon); + wxBitmapBundle* item_icon = get_bmp_bundle(it->second); + if (item_icon->IsOk()) + item->SetBitmap(*item_icon); } if (item->IsSubMenu()) for (wxMenuItem *sub_item : item->GetSubMenu()->GetMenuItems()) @@ -46,36 +43,24 @@ void msw_rescale_menu(wxMenu* menu) for (wxMenuItem *item : menu->GetMenuItems()) update_icons::run(item); } -#endif /* __WXMSW__ */ -#endif /* no __WXGTK__ */ +#endif /* no __linux__ */ void enable_menu_item(wxUpdateUIEvent& evt, std::function const cb_condition, wxMenuItem* item, wxWindow* win) { const bool enable = cb_condition(); evt.Enable(enable); - -#ifdef __WXOSX__ - const auto it = msw_menuitem_bitmaps.find(item->GetId()); - if (it != msw_menuitem_bitmaps.end()) - { - const wxBitmap& item_icon = create_scaled_bitmap(it->second, win, 16, !enable); - if (item_icon.IsOk()) - item->SetBitmap(item_icon); - } -#endif // __WXOSX__ } wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, -// std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler, - std::function cb, const wxBitmapBundle& icon, wxEvtHandler* event_handler, + std::function cb, wxBitmapBundle* icon, wxEvtHandler* event_handler, std::function const cb_condition, wxWindow* parent, int insert_pos/* = wxNOT_FOUND*/) { if (id == wxID_ANY) id = wxNewId(); auto *item = new wxMenuItem(menu, id, string, description); - if (icon.IsOk()) { - item->SetBitmap(icon); + if (icon && icon->IsOk()) { + item->SetBitmap(*icon); } if (insert_pos == wxNOT_FOUND) menu->Append(item); @@ -104,14 +89,12 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const if (id == wxID_ANY) id = wxNewId(); -// const wxBitmap& bmp = !icon.empty() ? create_menu_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr - const wxBitmapBundle& bmp = !icon.empty() ? create_menu_bitmap(icon) : wxNullBitmap; // FIXME: pass window ptr + wxBitmapBundle* bmp = icon.empty() ? nullptr : get_bmp_bundle(icon); -//#ifdef __WXMSW__ -#ifndef __WXGTK__ - if (bmp.IsOk()) +#ifndef __linux__ + if (bmp && bmp->IsOk()) msw_menuitem_bitmaps[id] = icon; -#endif /* __WXMSW__ */ +#endif /* no __linux__ */ return append_menu_item(menu, id, string, description, cb, bmp, event_handler, cb_condition, parent, insert_pos); } @@ -124,7 +107,7 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* item = new wxMenuItem(menu, id, string, description); if (!icon.empty()) { - item->SetBitmap(create_menu_bitmap(icon)); // FIXME: pass window ptr + item->SetBitmap(*get_bmp_bundle(icon)); //#ifdef __WXMSW__ #ifndef __WXGTK__ msw_menuitem_bitmaps[id] = icon; @@ -426,11 +409,34 @@ int mode_icon_px_size() #endif } -//wxBitmap create_menu_bitmap(const std::string& bmp_name) -wxBitmapBundle create_menu_bitmap(const std::string& bmp_name) +wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16*/) { - // return create_scaled_bitmap(bmp_name, nullptr, 16, false, "", true); - return wxBitmapBundle::FromSVGFile(Slic3r::var(bmp_name + ".svg"), wxSize(16, 16)); + static Slic3r::GUI::BitmapCache cache; + + std::string bmp_name = bmp_name_in; + boost::replace_last(bmp_name, ".png", ""); + + // Try loading an SVG first, then PNG if SVG is not found: + wxBitmapBundle* bmp = cache.from_svg(bmp_name, px_cnt, px_cnt, Slic3r::GUI::wxGetApp().dark_mode()); + if (bmp == nullptr) { + bmp = cache.from_png(bmp_name, px_cnt, px_cnt); + if (!bmp) + // Neither SVG nor PNG has been found, raise error + throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name); + } + return bmp; +} + +wxBitmapBundle* get_empty_bmp_bundle(int width, int height) +{ + static Slic3r::GUI::BitmapCache cache; + return cache.mkclear_bndl(width, height); +} + +wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color ) +{ + static Slic3r::GUI::BitmapCache cache; + return cache.mksolid_bndl(width, height, color, 1, Slic3r::GUI::wxGetApp().dark_mode()); } // win is used to get a correct em_unit value @@ -471,41 +477,19 @@ wxBitmap create_scaled_bitmap( const std::string& bmp_name_in, return *bmp; } -std::vector get_extruder_color_icons(bool thin_icon/* = false*/) +std::vector get_extruder_color_icons(bool thin_icon/* = false*/) { - static Slic3r::GUI::BitmapCache bmp_cache; - // Create the bitmap with color bars. - std::vector bmps; + std::vector bmps; std::vector colors = Slic3r::GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); if (colors.empty()) return bmps; - /* It's supposed that standard size of an icon is 36px*16px for 100% scaled display. - * So set sizes for solid_colored icons used for filament preset - * and scale them in respect to em_unit value - */ - const double em = Slic3r::GUI::wxGetApp().em_unit(); - const int icon_width = lround((thin_icon ? 1.6 : 3.2) * em); - const int icon_height = lround(1.6 * em); - bool dark_mode = Slic3r::GUI::wxGetApp().dark_mode(); for (const std::string& color : colors) - { - std::string bitmap_key = color + "-h" + std::to_string(icon_height) + "-w" + std::to_string(icon_width); - - wxBitmap* bitmap = bmp_cache.find(bitmap_key); - if (bitmap == nullptr) { - // Paint the color icon. - Slic3r::ColorRGB rgb; - Slic3r::decode_color(color, rgb); - // there is no neede to scale created solid bitmap - bitmap = bmp_cache.insert(bitmap_key, bmp_cache.mksolid(icon_width, icon_height, rgb, true, 1, dark_mode)); - } - bmps.emplace_back(bitmap); - } + bmps.emplace_back(get_solid_bmp_bundle(thin_icon ? 16 : 32, 16, color)); return bmps; } @@ -518,7 +502,7 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl, wxSize size/* = wxDefaultSize*/, bool use_thin_icon/* = false*/) { - std::vector icons = get_extruder_color_icons(use_thin_icon); + std::vector icons = get_extruder_color_icons(use_thin_icon); if (!*ctrl) { *ctrl = new Slic3r::GUI::BitmapComboBox(parent, wxID_ANY, wxEmptyString, pos, size, 0, nullptr, wxCB_READONLY); @@ -544,7 +528,7 @@ void apply_extruder_selector(Slic3r::GUI::BitmapComboBox** ctrl, int i = 0; wxString str = _(L("Extruder")); - for (wxBitmap* bmp : icons) { + for (wxBitmapBundle* bmp : icons) { if (i == 0) { if (!first_item.empty()) (*ctrl)->Append(_(first_item), *bmp); @@ -578,7 +562,7 @@ LockButton::LockButton( wxWindow *parent, Slic3r::GUI::wxGetApp().UpdateDarkUI(this); SetBitmap(m_bmp_lock_open.bmp()); SetBitmapDisabled(m_bmp_lock_open.bmp()); - SetBitmapHover(m_bmp_lock_closed_f.bmp()); + SetBitmapCurrent(m_bmp_lock_closed_f.bmp()); //button events Bind(wxEVT_BUTTON, &LockButton::OnButton, this); @@ -607,13 +591,14 @@ void LockButton::SetLock(bool lock) } } -void LockButton::msw_rescale() +void LockButton::sys_color_changed() { - return; - m_bmp_lock_closed.msw_rescale(); - m_bmp_lock_closed_f.msw_rescale(); - m_bmp_lock_open.msw_rescale(); - m_bmp_lock_open_f.msw_rescale(); + Slic3r::GUI::wxGetApp().UpdateDarkUI(this); + + m_bmp_lock_closed.sys_color_changed(); + m_bmp_lock_closed_f.sys_color_changed(); + m_bmp_lock_open.sys_color_changed(); + m_bmp_lock_open_f.sys_color_changed(); update_button_bitmaps(); } @@ -621,7 +606,7 @@ void LockButton::msw_rescale() void LockButton::update_button_bitmaps() { SetBitmap(m_is_pushed ? m_bmp_lock_closed.bmp() : m_bmp_lock_open.bmp()); - SetBitmapHover(m_is_pushed ? m_bmp_lock_closed_f.bmp() : m_bmp_lock_open_f.bmp()); + SetBitmapCurrent(m_is_pushed ? m_bmp_lock_closed_f.bmp() : m_bmp_lock_open_f.bmp()); Refresh(); Update(); @@ -648,8 +633,7 @@ ModeButton::ModeButton( wxWindow* parent, const wxString& mode/* = wxEmptyString*/, const std::string& icon_name/* = ""*/, int px_cnt/* = 16*/) : -// ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, icon_name, px_cnt), mode, wxBU_EXACTFIT) - ScalableButton(parent, wxID_ANY, icon_name, mode, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT) + ScalableButton(parent, wxID_ANY, icon_name, mode, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT, px_cnt) { Init(mode); } @@ -759,11 +743,10 @@ void ModeSizer::set_items_border(int border) item->SetBorder(border); } -void ModeSizer::msw_rescale() +void ModeSizer::sys_color_changed() { - this->SetHGap(std::lround(m_hgap_unscaled * em_unit(m_parent))); for (size_t m = 0; m < m_mode_btns.size(); m++) - m_mode_btns[m]->msw_rescale(); + m_mode_btns[m]->sys_color_changed(); } // ---------------------------------------------------------------------------- @@ -803,40 +786,13 @@ ScalableBitmap::ScalableBitmap( wxWindow *parent, m_parent(parent), m_icon_name(icon_name), m_px_cnt(px_cnt) { - m_bmp = create_scaled_bitmap(icon_name, parent, px_cnt, grayscale); + m_bmp = *get_bmp_bundle(icon_name, px_cnt); + m_bitmap = m_bmp.GetBitmapFor(m_parent); } -wxSize ScalableBitmap::GetBmpSize() const +void ScalableBitmap::sys_color_changed() { -#ifdef __APPLE__ - return m_bmp.GetScaledSize(); -#else - return m_bmp.GetSize(); -#endif -} - -int ScalableBitmap::GetBmpWidth() const -{ -#ifdef __APPLE__ - return m_bmp.GetScaledWidth(); -#else - return m_bmp.GetWidth(); -#endif -} - -int ScalableBitmap::GetBmpHeight() const -{ -#ifdef __APPLE__ - return m_bmp.GetScaledHeight(); -#else - return m_bmp.GetHeight(); -#endif -} - - -void ScalableBitmap::msw_rescale() -{ - m_bmp = create_scaled_bitmap(m_icon_name, m_parent, m_px_cnt, m_grayscale); + m_bmp = *get_bmp_bundle(m_icon_name, m_px_cnt); } // ---------------------------------------------------------------------------- @@ -850,11 +806,9 @@ ScalableButton::ScalableButton( wxWindow * parent, const wxSize& size /* = wxDefaultSize*/, const wxPoint& pos /* = wxDefaultPosition*/, long style /*= wxBU_EXACTFIT | wxNO_BORDER*/, - bool use_default_disabled_bitmap/* = false*/, int bmp_px_cnt/* = 16*/) : m_parent(parent), m_current_icon_name(icon_name), - m_use_default_disabled_bitmap (use_default_disabled_bitmap), m_px_cnt(bmp_px_cnt), m_has_border(!(style & wxNO_BORDER)) { @@ -862,10 +816,7 @@ ScalableButton::ScalableButton( wxWindow * parent, Slic3r::GUI::wxGetApp().UpdateDarkUI(this); if (!icon_name.empty()) { -// SetBitmap(create_scaled_bitmap(icon_name, parent, m_px_cnt)); - SetBitmap(wxBitmapBundle::FromSVGFile(Slic3r::var(icon_name + ".svg"), wxSize(m_px_cnt, m_px_cnt))); - //if (m_use_default_disabled_bitmap) - // SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); + SetBitmap(*get_bmp_bundle(icon_name, m_px_cnt)); if (!label.empty()) SetBitmapMargins(int(0.5* em_unit(parent)), 0); } @@ -907,14 +858,12 @@ bool ScalableButton::SetBitmap_(const std::string& bmp_name) if (m_current_icon_name.empty()) return false; -// wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); - wxBitmapBundle bmp = wxBitmapBundle::FromSVGFile(Slic3r::var(m_current_icon_name + ".svg"), wxSize(16, 16)); + wxBitmapBundle bmp = *get_bmp_bundle(m_current_icon_name, m_px_cnt); SetBitmap(bmp); SetBitmapCurrent(bmp); SetBitmapPressed(bmp); SetBitmapFocus(bmp); - if (m_use_default_disabled_bitmap) - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); + SetBitmapDisabled(bmp); return true; } @@ -933,38 +882,21 @@ int ScalableButton::GetBitmapHeight() #endif } -void ScalableButton::UseDefaultBitmapDisabled() -{ - m_use_default_disabled_bitmap = true; - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); -} - -void ScalableButton::msw_rescale() +void ScalableButton::sys_color_changed() { Slic3r::GUI::wxGetApp().UpdateDarkUI(this, m_has_border); -// if (!m_current_icon_name.empty()) { - if (0) { - wxBitmap bmp = create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt); - SetBitmap(bmp); - SetBitmapCurrent(bmp); - SetBitmapPressed(bmp); - SetBitmapFocus(bmp); - if (!m_disabled_icon_name.empty()) - SetBitmapDisabled(create_scaled_bitmap(m_disabled_icon_name, m_parent, m_px_cnt)); - else if (m_use_default_disabled_bitmap) - SetBitmapDisabled(create_scaled_bitmap(m_current_icon_name, m_parent, m_px_cnt, true)); - } - - if (m_width > 0 || m_height>0) - { - const int em = em_unit(m_parent); - wxSize size(m_width * em, m_height * em); - SetMinSize(size); - } + wxBitmapBundle bmp = *get_bmp_bundle(m_current_icon_name, m_px_cnt); + SetBitmap(bmp); + SetBitmapCurrent(bmp); + SetBitmapPressed(bmp); + SetBitmapFocus(bmp); + if (!m_disabled_icon_name.empty()) + SetBitmapDisabled(*get_bmp_bundle(m_disabled_icon_name, m_px_cnt)); + if (!GetLabelText().IsEmpty()) + SetBitmapMargins(int(0.5 * em_unit(m_parent)), 0); } - // ---------------------------------------------------------------------------- // BlinkingBitmap // ---------------------------------------------------------------------------- @@ -975,13 +907,6 @@ BlinkingBitmap::BlinkingBitmap(wxWindow* parent, const std::string& icon_name) : bmp = ScalableBitmap(parent, icon_name); } -void BlinkingBitmap::msw_rescale() -{ - bmp.msw_rescale(); - this->SetSize(bmp.GetBmpSize()); - this->SetMinSize(bmp.GetBmpSize()); -} - void BlinkingBitmap::invalidate() { this->SetBitmap(wxNullBitmap); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 8387551f2..a22622d39 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -16,15 +16,14 @@ #include -#ifdef __WXMSW__ -void msw_rescale_menu(wxMenu* menu); -#else /* __WXMSW__ */ -inline void msw_rescale_menu(wxMenu* /* menu */) {} -#endif /* __WXMSW__ */ +#ifndef __linux__ +void sys_color_changed_menu(wxMenu* menu); +#else +inline void sys_color_changed_menu(wxMenu* /* menu */) {} +#endif // no __linux__ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, -// std::function cb, const wxBitmap& icon, wxEvtHandler* event_handler = nullptr, - std::function cb, const wxBitmapBundle& icon, wxEvtHandler* event_handler = nullptr, + std::function cb, wxBitmapBundle* icon, wxEvtHandler* event_handler = nullptr, std::function const cb_condition = []() { return true;}, wxWindow* parent = nullptr, int insert_pos = wxNOT_FOUND); wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr, @@ -51,15 +50,16 @@ void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector< int em_unit(wxWindow* win); int mode_icon_px_size(); -//wxBitmap create_menu_bitmap(const std::string& bmp_name); -wxBitmapBundle create_menu_bitmap(const std::string& bmp_name); +wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name, int px_cnt = 16); +wxBitmapBundle* get_empty_bmp_bundle(int width, int height); +wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color); wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr, const int px_cnt = 16, const bool grayscale = false, const std::string& new_color = std::string(), // color witch will used instead of orange const bool menu_bitmap = false); -std::vector get_extruder_color_icons(bool thin_icon = false); +std::vector get_extruder_color_icons(bool thin_icon = false); namespace Slic3r { namespace GUI { @@ -148,21 +148,28 @@ public: ~ScalableBitmap() {} - wxSize GetBmpSize() const; - int GetBmpWidth() const; - int GetBmpHeight() const; + void sys_color_changed(); - void msw_rescale(); + const wxBitmapBundle& bmp() const { return m_bmp; } + wxBitmap get_bitmap() { return m_bmp.GetBitmapFor(m_parent); } + wxWindow* parent() const { return m_parent;} + const std::string& name() const { return m_icon_name; } + int px_cnt() const { return m_px_cnt;} - const wxBitmap& bmp() const { return m_bmp; } - wxBitmap& bmp() { return m_bmp; } - const std::string& name() const{ return m_icon_name; } - - int px_cnt()const {return m_px_cnt;} + wxSize GetSize() const { +#ifdef __APPLE__ + return m_bmp.GetDefaultSize(); +#else + return m_bmp.GetPreferredBitmapSizeFor(m_parent); +#endif + } + int GetWidth() const { return GetSize().GetWidth(); } + int GetHeight() const { return GetSize().GetHeight(); } private: wxWindow* m_parent{ nullptr }; - wxBitmap m_bmp = wxBitmap(); + wxBitmapBundle m_bmp = wxBitmapBundle(); + wxBitmap m_bitmap = wxBitmap(); std::string m_icon_name = ""; int m_px_cnt {16}; bool m_grayscale {false}; @@ -192,7 +199,7 @@ public: void enable() { m_disabled = false; } void disable() { m_disabled = true; } - void msw_rescale(); + void sys_color_changed(); protected: void update_button_bitmaps(); @@ -224,7 +231,6 @@ public: const wxSize& size = wxDefaultSize, const wxPoint& pos = wxDefaultPosition, long style = wxBU_EXACTFIT | wxNO_BORDER, - bool use_default_disabled_bitmap = false, int bmp_px_cnt = 16); ScalableButton( @@ -240,9 +246,8 @@ public: bool SetBitmap_(const std::string& bmp_name); void SetBitmapDisabled_(const ScalableBitmap &bmp); int GetBitmapHeight(); - void UseDefaultBitmapDisabled(); - void msw_rescale(); + void sys_color_changed(); private: wxWindow* m_parent { nullptr }; @@ -251,8 +256,6 @@ private: int m_width {-1}; // should be multiplied to em_unit int m_height{-1}; // should be multiplied to em_unit - bool m_use_default_disabled_bitmap {false}; - // bitmap dimensions int m_px_cnt{ 16 }; bool m_has_border {false}; @@ -318,7 +321,7 @@ public: void set_items_flag(int flag); void set_items_border(int border); - void msw_rescale(); + void sys_color_changed(); const std::vector& get_btns() { return m_mode_btns; } private: @@ -366,12 +369,11 @@ public: ~BlinkingBitmap() {} - void msw_rescale(); void invalidate(); void activate(); void blink(); - const wxBitmap& get_bmp() const { return bmp.bmp(); } + const wxBitmapBundle& get_bmp() const { return bmp.bmp(); } private: ScalableBitmap bmp; From ae08819a2430e0764fab69cc0d03c50820e046a8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 3 Jun 2022 12:49:21 +0200 Subject: [PATCH 41/93] Using of wxWidgets 3.1.6 WIP: Linux/OSX specific fixes OSX specific: Fixed get_mouse_position_in_control(). + Use GetItemRect() to calculation of the position and size for Extruder selector Linux specific: * Use just 1.0 scale for wxBitmapComboboxes under GTK3 and gtk3 * GTK2 specific: use GTK2 doesn't suppost get_scale, so scale bitmap size form em_unit() --- src/slic3r/GUI/BitmapCache.cpp | 129 ++++------------------------ src/slic3r/GUI/BitmapCache.hpp | 7 -- src/slic3r/GUI/ExtraRenderers.cpp | 23 +++-- src/slic3r/GUI/GUI_ObjectList.cpp | 26 ++++-- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/OG_CustomCtrl.cpp | 6 +- src/slic3r/GUI/PresetComboBoxes.cpp | 9 +- src/slic3r/GUI/wxExtensions.cpp | 27 ++++-- src/slic3r/GUI/wxExtensions.hpp | 6 +- 9 files changed, 85 insertions(+), 150 deletions(-) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index d2bf98b5b..147615277 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -65,6 +65,8 @@ wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vec wxVector bitmaps; std::set scales = {1.0}; +#ifndef __linux__ + #ifdef __APPLE__ scales.emplace(m_scale); #else @@ -73,12 +75,14 @@ wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vec scales.emplace(wxDisplay(disp).GetScaleFactor()); #endif +#endif // !__linux__ + for (double scale : scales) { size_t width = 0; size_t height = 0; for (const wxBitmapBundle* bmp_bndl : bmps) { #ifdef __APPLE__ - wxSize size = bmp_bndl->GetPreferredBitmapSizeAtScale(1.0); + wxSize size = bmp_bndl->GetDefaultSize(); #else wxSize size = bmp_bndl->GetPreferredBitmapSizeAtScale(scale); #endif @@ -98,7 +102,7 @@ wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vec memset(image.GetAlpha(), 0, width * height); size_t x = 0; for (const wxBitmapBundle* bmp_bndl : bmps) { - wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetPreferredBitmapSizeAtScale(scale)); + wxBitmap bmp = bmp_bndl->GetBitmap(bmp_bndl->GetDefaultSize()); if (bmp.GetWidth() > 0) { if (bmp.GetDepth() == 32) { wxAlphaPixelData data(bmp); @@ -138,7 +142,7 @@ wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vec } } } - x += bmp.GetWidth(); + x += bmp.GetScaledWidth(); } bitmaps.push_back(* this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)))); @@ -156,8 +160,8 @@ wxBitmapBundle* BitmapCache::insert_bndl(const std::string& name, const std::vec if (bmp.GetWidth() > 0) memDC.DrawBitmap(bmp, x, 0, true); -#ifdef __APPLE__ // we should "move" with step equal to non-scaled width +#ifdef __APPLE__ x += bmp.GetScaledWidth(); #else x += bmp.GetWidth(); @@ -262,110 +266,6 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp return bitmap; } -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[2] = { bmp, bmp2 }; - return this->insert(bitmap_key, bmps, bmps + 2); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[3] = { bmp, bmp2, bmp3 }; - return this->insert(bitmap_key, bmps, bmps + 3); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end) -{ - size_t width = 0; - size_t height = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { -#ifdef __APPLE__ - width += bmp->GetScaledWidth(); - height = std::max(height, bmp->GetScaledHeight()); -#else - width += bmp->GetWidth(); - height = std::max(height, bmp->GetHeight()); -#endif - } - -#ifdef __WXGTK2__ - // Broken alpha workaround - wxImage image(width, height); - image.InitAlpha(); - // Fill in with a white color. - memset(image.GetData(), 0x0ff, width * height * 3); - // Fill in with full transparency. - memset(image.GetAlpha(), 0, width * height); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) { - if (bmp->GetDepth() == 32) { - wxAlphaPixelData data(*const_cast(bmp)); - //FIXME The following method is missing from wxWidgets 3.1.1. - // It looks like the wxWidgets 3.0.3 called the wrapped bitmap's UseAlpha(). - //data.UseAlpha(); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxAlphaPixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = src.Alpha(); - } - } - } - } else if (bmp->GetDepth() == 24) { - wxNativePixelData data(*const_cast(bmp)); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxNativePixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = wxALPHA_OPAQUE; - } - } - } - } - } - x += bmp->GetWidth(); - } - return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); - -#else - - wxBitmap *bitmap = this->insert(bitmap_key, width, height); - wxMemoryDC memDC; - memDC.SelectObject(*bitmap); - memDC.SetBackground(*wxTRANSPARENT_BRUSH); - memDC.Clear(); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) - memDC.DrawBitmap(*bmp, x, 0, true); -#ifdef __APPLE__ - // we should "move" with step equal to non-scaled width - x += bmp->GetScaledWidth(); -#else - x += bmp->GetWidth(); -#endif - } - memDC.SelectObject(wxNullBitmap); - return bitmap; - -#endif -} - wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale/* = false*/) { wxImage image(width, height); @@ -492,7 +392,6 @@ wxBitmapBundle* BitmapCache::from_svg(const std::string& bitmap_name, unsigned t std::string bitmap_key = bitmap_name + (target_height != 0 ? "-h" + std::to_string(target_height) : "-w" + std::to_string(target_width)) -// + (m_scale != 1.0f ? "-s" + float_to_string_decimal_point(m_scale) : "") + (dark_mode ? "-dm" : "") + new_color; @@ -594,9 +493,9 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_ return this->insert_raw_rgba(bitmap_key, width, height, data.data(), grayscale); } - +/* //we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap -wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false*/, size_t border_width /*= 0*/, bool dark_mode/* = false*/) +wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling/* = false* /, size_t border_width /*= 0* /, bool dark_mode/* = false* /) { double scale = suppress_scaling ? 1.0f : m_scale; width *= scale; @@ -638,13 +537,15 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi return wxImage_to_wxBitmap_with_alpha(std::move(image), scale); } - +*/ //we make scaled solid bitmaps only for the cases, when its will be used with scaled SVG icon in one output bitmap wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width /*= 0*/, bool dark_mode/* = false*/) { wxVector bitmaps; std::set scales = { 1.0 }; +#ifndef __linux__ + #ifdef __APPLE__ scales.emplace(m_scale); #else @@ -653,6 +554,8 @@ wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned scales.emplace(wxDisplay(disp).GetScaleFactor()); #endif +#endif // !__linux__ + for (double scale : scales) { size_t width = width_in * scale; size_t height = height_in * scale; @@ -698,7 +601,7 @@ wxBitmapBundle BitmapCache::mksolid(size_t width_in, size_t height_in, unsigned wxBitmapBundle* BitmapCache::mksolid_bndl(size_t width, size_t height, const std::string& color, size_t border_width, bool dark_mode) { - std::string bitmap_key = (color.empty() ? "empty-w" : color) + "-h" + std::to_string(height) + "-w" + std::to_string(width) + (dark_mode ? "-dm" : ""); + std::string bitmap_key = (color.empty() ? "empty" : color) + "-h" + std::to_string(height) + "-w" + std::to_string(width) + (dark_mode ? "-dm" : ""); wxBitmapBundle* bndl = nullptr; auto it = m_bndl_map.find(bitmap_key); diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 28058d94b..6ba9ae15b 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -37,10 +37,6 @@ public: wxBitmap* insert(const std::string &name, size_t width, size_t height, double scale = -1.0); wxBitmap* insert(const std::string &name, const wxBitmap &bmp); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); - wxBitmap* insert(const std::string &name, const std::vector &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } - wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, const bool grayscale = false); // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. @@ -58,9 +54,6 @@ public: // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false, const std::string& new_color = ""); - wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false); - wxBitmap mksolid(size_t width, size_t height, const ColorRGB& rgb, bool suppress_scaling = false, size_t border_width = 0, bool dark_mode = false) { return mksolid(width, height, rgb.r_uchar(), rgb.g_uchar(), rgb.b_uchar(), wxALPHA_OPAQUE, suppress_scaling, border_width, dark_mode); } - wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT, true, 0); } wxBitmapBundle mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, size_t border_width = 0, bool dark_mode = false); wxBitmapBundle* mksolid_bndl(size_t width, size_t height, const std::string& color = std::string(), size_t border_width = 0, bool dark_mode = false); wxBitmapBundle* mkclear_bndl(size_t width, size_t height) { return mksolid_bndl(width, height); } diff --git a/src/slic3r/GUI/ExtraRenderers.cpp b/src/slic3r/GUI/ExtraRenderers.cpp index 9bccb6b63..0368a2fe2 100644 --- a/src/slic3r/GUI/ExtraRenderers.cpp +++ b/src/slic3r/GUI/ExtraRenderers.cpp @@ -33,6 +33,15 @@ wxIMPLEMENT_DYNAMIC_CLASS(DataViewBitmapText, wxObject) IMPLEMENT_VARIANT_OBJECT(DataViewBitmapText) +static wxSize get_size(const wxBitmap& icon) +{ +#ifdef __WIN32__ + return icon.GetSize(); +#else + return icon.GetScaledSize(); +#endif +} + // --------------------------------------------------------- // BitmapTextRenderer // --------------------------------------------------------- @@ -124,11 +133,7 @@ bool BitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) const wxBitmap& icon = m_value.GetBitmap(); if (icon.IsOk()) { -#ifdef __APPLE__ - wxSize icon_sz = icon.GetScaledSize(); -#else - wxSize icon_sz = icon.GetSize(); -#endif + wxSize icon_sz = get_size(icon); dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.y) / 2); xoffset = icon_sz.x + 4; } @@ -264,11 +269,13 @@ bool BitmapChoiceRenderer::Render(wxRect rect, wxDC* dc, int state) const wxBitmap& icon = m_value.GetBitmap(); if (icon.IsOk()) { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; + wxSize icon_sz = get_size(icon); + + dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon_sz.GetHeight()) / 2); + xoffset = icon_sz.GetWidth() + 4; if (rect.height==0) - rect.height= icon.GetHeight(); + rect.height= icon_sz.GetHeight(); } #ifdef _WIN32 diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 1171149b6..da8f83388 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -971,12 +971,11 @@ void ObjectList::extruder_editing() if (!item || !(m_objects_model->GetItemType(item) & (itVolume | itObject))) return; - const int column_width = GetColumn(colExtruder)->GetWidth() + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X) + 5; - - wxPoint pos = this->get_mouse_position_in_control(); - wxSize size = wxSize(column_width, -1); - pos.x = GetColumn(colName)->GetWidth() + GetColumn(colPrint)->GetWidth() + 5; - pos.y -= GetTextExtent("m").y; + wxRect rect = this->GetItemRect(item, GetColumn(colExtruder)); + wxPoint pos = rect.GetPosition(); + pos.y -= 4; + wxSize size = rect.GetSize(); + size.SetWidth(size.GetWidth() + 8); apply_extruder_selector(&m_extruder_editor, this, L("default"), pos, size); @@ -2433,6 +2432,21 @@ bool ObjectList::can_merge_to_single_object() const return (*m_objects)[obj_idx]->volumes.size() > 1; } +wxPoint ObjectList::get_mouse_position_in_control() const +{ + wxPoint pt = wxGetMousePosition() - this->GetScreenPosition(); + +#ifdef __APPLE__ + // Workaround for OSX. From wxWidgets 3.1.6 Hittest doesn't respect to the header of wxDataViewCtrl + if (wxDataViewItem top_item = this->GetTopItem(); top_item.IsOk()) { + auto rect = this->GetItemRect(top_item, this->GetColumn(0)); + pt.y -= rect.y; + } +#endif // __APPLE__ + + return pt; +} + // NO_PARAMETERS function call means that changed object index will be determine from Selection() void ObjectList::changed_object(const int obj_idx/* = -1*/) const { diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index b9b816b7b..c838203ae 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -280,7 +280,7 @@ public: bool can_merge_to_multipart_object() const; bool can_merge_to_single_object() const; - wxPoint get_mouse_position_in_control() const { return wxGetMousePosition() - this->GetScreenPosition(); } + wxPoint get_mouse_position_in_control() const; wxBoxSizer* get_sizer() {return m_sizer;} int get_selected_obj_idx() const; ModelConfig& get_item_config(const wxDataViewItem& item) const; diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index c202de5e2..2c367d62a 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -21,10 +21,10 @@ static bool is_point_in_rect(const wxPoint& pt, const wxRect& rect) static wxSize get_bitmap_size(const wxBitmapBundle* bmp, wxWindow* parent) { -#ifdef __APPLE__ - return bmp->GetDefaultSize(); -#else +#ifdef __WIN32__ return bmp->GetBitmapFor(parent).GetSize(); +#else + return bmp->GetDefaultSize(); #endif } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 93a5fe433..597c30312 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -389,14 +389,14 @@ void PresetComboBox::sys_color_changed() void PresetComboBox::fill_width_height() { - icon_height = m_bitmapCompatible->GetPreferredBitmapSizeAtScale(1.0).GetHeight(); - norm_icon_width = m_bitmapCompatible->GetPreferredBitmapSizeAtScale(1.0).GetWidth(); - - null_icon_width = 2 * norm_icon_width; + icon_height = 16; + norm_icon_width = 16; thin_icon_width = 8; wide_icon_width = norm_icon_width + thin_icon_width; + null_icon_width = 2 * norm_icon_width; + space_icon_width = 2; thin_space_icon_width = 4; wide_space_icon_width = 6; @@ -484,6 +484,7 @@ wxBitmapBundle* PresetComboBox::get_bmp( std::string bitmap_key, const std::str wxBitmapBundle PresetComboBox::NullBitmapBndl() { + assert(null_icon_width > 0); return *get_empty_bmp_bundle(null_icon_width, icon_height); } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 67890bac8..c397b4b39 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -108,10 +108,10 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* item = new wxMenuItem(menu, id, string, description); if (!icon.empty()) { item->SetBitmap(*get_bmp_bundle(icon)); -//#ifdef __WXMSW__ -#ifndef __WXGTK__ + +#ifndef __linux__ msw_menuitem_bitmaps[id] = icon; -#endif /* __WXMSW__ */ +#endif // no __linux__ } item->SetSubMenu(sub_menu); @@ -409,8 +409,19 @@ int mode_icon_px_size() #endif } +#ifdef __WXGTK2__ +static int scale() +{ + return int(em_unit(nullptr) * 0.1f + 0.5f); +} +#endif // __WXGTK2__ + wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16*/) { +#ifdef __WXGTK2__ + px_cnt *= scale(); +#endif // __WXGTK2__ + static Slic3r::GUI::BitmapCache cache; std::string bmp_name = bmp_name_in; @@ -430,13 +441,21 @@ wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name_in, int px_cnt/* = 16 wxBitmapBundle* get_empty_bmp_bundle(int width, int height) { static Slic3r::GUI::BitmapCache cache; +#ifdef __WXGTK2__ + return cache.mkclear_bndl(width * scale(), height * scale()); +#else return cache.mkclear_bndl(width, height); +#endif // __WXGTK2__ } wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color ) { static Slic3r::GUI::BitmapCache cache; +#ifdef __WXGTK2__ + return cache.mksolid_bndl(width * scale(), height * scale(), color, 1, Slic3r::GUI::wxGetApp().dark_mode()); +#else return cache.mksolid_bndl(width, height, color, 1, Slic3r::GUI::wxGetApp().dark_mode()); +#endif // __WXGTK2__ } // win is used to get a correct em_unit value @@ -486,8 +505,6 @@ std::vector get_extruder_color_icons(bool thin_icon/* = false*/ if (colors.empty()) return bmps; - bool dark_mode = Slic3r::GUI::wxGetApp().dark_mode(); - for (const std::string& color : colors) bmps.emplace_back(get_solid_bmp_bundle(thin_icon ? 16 : 32, 16, color)); diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index a22622d39..db05af9eb 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -157,10 +157,10 @@ public: int px_cnt() const { return m_px_cnt;} wxSize GetSize() const { -#ifdef __APPLE__ - return m_bmp.GetDefaultSize(); -#else +#ifdef __WIN32__ return m_bmp.GetPreferredBitmapSizeFor(m_parent); +#else + return m_bmp.GetDefaultSize(); #endif } int GetWidth() const { return GetSize().GetWidth(); } From b6e6be27c073ed5c316e6796e153b1c63916b179 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 22 Jun 2022 15:57:56 +0200 Subject: [PATCH 42/93] wxWidgets.cmake: Changed url and SHA to use next commit in our wxWidgets patch --- deps/wxWidgets/wxWidgets.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 4a0875d62..96d56c0bc 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -13,8 +13,8 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for endif() prusaslicer_add_cmake_project(wxWidgets - URL https://github.com/prusa3d/wxWidgets/archive/5412ac15586da3ecb6952fcc875d2a23366c998f.zip - URL_HASH SHA256=85a6e13152289fbf1ea51f221fbe1452e7914bbaa665b89536780810e93948a6 + URL https://github.com/prusa3d/wxWidgets/archive/e616df45b3a8aedc31beb5540df793dd1f0f3914.zip + URL_HASH SHA256=30bc6cad64dce5cdc755e3a4119cfeb24c12d43279e1e062d8ac350d3880f315 DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG CMAKE_ARGS -DwxBUILD_PRECOMP=ON From 40afbe6371b2057c16100f6e80b8bb7d6c6893ae Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 19 Jan 2022 13:24:37 +0100 Subject: [PATCH 43/93] Update to GLEW 2.2 to prevent initialization crash with wx >= 3.1.6 Revert "Revert to GLEW 2.1 as most Linux distros as using that" This reverts commit 46c8f82f24127dc992e904b9b3f1c75a719f0491. --- deps/GLEW/GLEW.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/GLEW/GLEW.cmake b/deps/GLEW/GLEW.cmake index ae2832577..76ffc0a8d 100644 --- a/deps/GLEW/GLEW.cmake +++ b/deps/GLEW/GLEW.cmake @@ -4,8 +4,8 @@ find_package(OpenGL QUIET REQUIRED) prusaslicer_add_cmake_project( GLEW - URL https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip - URL_HASH SHA256=2700383d4de2455f06114fbaf872684f15529d4bdc5cdea69b5fb0e9aa7763f1 + URL https://sourceforge.net/projects/glew/files/glew/2.2.0/glew-2.2.0.zip + URL_HASH SHA256=a9046a913774395a095edcc0b0ac2d81c3aacca61787b39839b941e9be14e0d4 SOURCE_SUBDIR build/cmake CMAKE_ARGS -DBUILD_UTILS=OFF From 0bc707e540d77d576405bf799f3635973ce1d55d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 27 Jun 2022 09:25:38 +0200 Subject: [PATCH 44/93] Tried to disable EGL. --- deps/wxWidgets/wxWidgets.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 96d56c0bc..e2d48ce65 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -37,6 +37,7 @@ prusaslicer_add_cmake_project(wxWidgets -DwxUSE_EXPAT=sys -DwxUSE_LIBSDL=OFF -DwxUSE_XTEST=OFF + -DwxUSE_GLCANVAS_EGL=OFF ) if (MSVC) From 3d03ef015f435852fd1ae1961471c2e8aade77c6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 27 Jun 2022 14:27:26 +0200 Subject: [PATCH 45/93] OSX specific: Fixed a warning --- src/slic3r/GUI/GUI_App.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0412bca21..42e325de7 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2088,7 +2088,15 @@ bool GUI_App::load_language(wxString language, bool initial) { // Allocating a temporary locale will switch the default wxTranslations to its internal wxTranslations instance. wxLocale temp_locale; +#ifdef __WXOSX__ + // ysFIXME - temporary workaround till it isn't fixed in wxWidgets: + // Use English as an initial language, because of under OSX it try to load "inappropriate" language for wxLANGUAGE_DEFAULT. + // For example in our case it's trying to load "en_CZ" and as a result PrusaSlicer catch warning message. + // But wxWidgets guys work on it. + temp_locale.Init(wxLANGUAGE_ENGLISH); +#else temp_locale.Init(); +#endif // __WXOSX__ // Set the current translation's language to default, otherwise GetBestTranslation() may not work (see the wxWidgets source code). wxTranslations::Get()->SetLanguage(wxLANGUAGE_DEFAULT); // Let the wxFileTranslationsLoader enumerate all translation dictionaries for PrusaSlicer From 4b214598a2375109482bf9c79c9d3df2b8c00dfd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 29 Jun 2022 13:42:10 +0200 Subject: [PATCH 46/93] After merge fixes --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 45 ++++++-------------------- src/slic3r/GUI/ObjectDataViewModel.hpp | 6 ++-- 3 files changed, 12 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 350f8cdc0..972598b73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -980,7 +980,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) // for (Axis axis : {X, Y, Z}) // render_rotation_input(axis); - // m_imgui->text(_L("°")); + // m_imgui->text(_L("°")); // ImGui::Separator(); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 23668ee99..a6a767faa 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -58,7 +58,6 @@ const std::map INFO_ITEMS{ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmapBundle& bmp, const wxString& extruder, const int idx/* = -1*/) : m_parent(parent), @@ -319,7 +318,7 @@ ObjectDataViewModel::ObjectDataViewModel() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = *get_bmp_bundle(WarningIcon); m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon); - m_lock_bmp = create_scaled_bitmap(LockIcon); + m_lock_bmp = *get_bmp_bundle(LockIcon); for (auto item : INFO_ITEMS) m_info_bmps[item.first] = get_bmp_bundle(item.second.bmp_name); @@ -333,14 +332,13 @@ ObjectDataViewModel::~ObjectDataViewModel() m_bitmap_cache = nullptr; } -//wxBitmapBundle& ObjectDataViewModel::GetWarningBitmap(const std::string& warning_icon_name) void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node) { int vol_type = static_cast(node->GetVolumeType()); bool is_volume_node = vol_type >= 0; if (!node->has_warning_icon() && !node->has_lock()) { - node->SetBitmap(is_volume_node ? m_volume_bmps[vol_type] : m_empty_bmp); + node->SetBitmap(is_volume_node ? *m_volume_bmps.at(vol_type) : m_empty_bmp); return; } @@ -351,18 +349,18 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node) scaled_bitmap_name += LockIcon; if (is_volume_node) scaled_bitmap_name += std::to_string(vol_type); - scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm"); + scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "-lm"); - wxBitmap* bmp = m_bitmap_cache->find(scaled_bitmap_name); + wxBitmapBundle* bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); if (!bmp) { - std::vector bmps; + std::vector bmps; if (node->has_warning_icon()) - bmps.emplace_back(node->warning_icon_name() == WarningIcon ? m_warning_bmp : m_warning_manifold_bmp); + bmps.emplace_back(node->warning_icon_name() == WarningIcon ? &m_warning_bmp : &m_warning_manifold_bmp); if (node->has_lock()) - bmps.emplace_back(m_lock_bmp); + bmps.emplace_back(&m_lock_bmp); if (is_volume_node) bmps.emplace_back(m_volume_bmps[vol_type]); - bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps); + bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps); } node->SetBitmap(*bmp); @@ -1716,7 +1714,7 @@ void ObjectDataViewModel::UpdateBitmaps() m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = *get_bmp_bundle(WarningIcon); m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon); - m_lock_bmp = create_scaled_bitmap(LockIcon); + m_lock_bmp = *get_bmp_bundle(LockIcon); for (auto item : INFO_ITEMS) m_info_bmps[item.first] = get_bmp_bundle(item.second.bmp_name); @@ -1751,28 +1749,7 @@ void ObjectDataViewModel::UpdateBitmaps() ItemChanged(item); } } -/* -wxBitmapBundle ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const std::string& warning_icon_name/* = std::string()*/) -{ - if (warning_icon_name.empty()) - return *m_volume_bmps[static_cast(vol_type)]; - std::string scaled_bitmap_name = warning_icon_name + std::to_string(static_cast(vol_type)); - scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "-lm"); - - wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); - if (bmp == nullptr) { - std::vector bmps; - - bmps.emplace_back(&GetWarningBitmap(warning_icon_name)); - bmps.emplace_back(m_volume_bmps[static_cast(vol_type)]); - - bmp = m_bitmap_cache->insert_bndl(scaled_bitmap_name, bmps); - } - - return *bmp; -} -*/ void ObjectDataViewModel::AddWarningIcon(const wxDataViewItem& item, const std::string& warning_icon_name) { if (!item.IsOk()) @@ -1804,10 +1781,6 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo node->SetWarningIconName(std::string()); UpdateBitmapForNode(node); -/* if (node->GetType() & itVolume) { - node->SetWarningBitmap(*m_volume_bmps[static_cast(node->volume_type())], ""); - return; - }*/ if (unmark_object) { diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 995fb1033..87be7a4e2 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -101,7 +101,6 @@ public: ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const wxString& sub_obj_name, Slic3r::ModelVolumeType type, - const wxBitmapBundle& bmp, const wxString& extruder, const int idx = -1 ); @@ -182,7 +181,7 @@ public: void SetVolumeType(ModelVolumeType type) { m_volume_type = type; } void SetBitmap(const wxBitmapBundle &icon) { m_bmp = icon; } void SetExtruder(const wxString &extruder) { m_extruder = extruder; } - void SetWarningBitmap(const wxBitmapBundle& icon, const std::string& warning_icon_name) { /*m_bmp = icon; */m_warning_icon_name = warning_icon_name; } + void SetWarningIconName(const std::string& warning_icon_name) { m_warning_icon_name = warning_icon_name; } void SetLock(bool has_lock) { m_has_lock = has_lock; } const wxBitmapBundle& GetBitmap() const { return m_bmp; } const wxString& GetName() const { return m_name; } @@ -264,7 +263,7 @@ class ObjectDataViewModel :public wxDataViewModel wxBitmapBundle m_empty_bmp; wxBitmapBundle m_warning_bmp; wxBitmapBundle m_warning_manifold_bmp; - wxBitmap m_lock_bmp; + wxBitmapBundle m_lock_bmp; wxDataViewCtrl* m_ctrl { nullptr }; @@ -408,7 +407,6 @@ private: wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item); void AddAllChildren(const wxDataViewItem& parent); -// wxBitmapBundle& GetWarningBitmap(const std::string& warning_icon_name); void UpdateBitmapForNode(ObjectDataViewModelNode* node); void UpdateBitmapForNode(ObjectDataViewModelNode* node, const std::string& warning_icon_name, bool has_lock); }; From 07d455a12566f0c37fe6d58c8c5eb7a5382ba7b2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 11 Jul 2022 09:11:06 +0200 Subject: [PATCH 47/93] Cut WIP: some UI improvements + partially reverted https://github.com/prusa3d/PrusaSlicer/commit/63890b5f8d352d3ef1228fa00b0d3932717f933d --- src/slic3r/GUI/GLCanvas3D.cpp | 10 ++- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GUI_ObjectList.cpp | 5 ++ src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 4 ++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 84 ++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 6 +- 7 files changed, 84 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3a5c89bea..cbf1a61d4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1382,6 +1382,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) && (mv == nullptr || m_model->objects[vol->composite_id.object_id]->volumes[vol->composite_id.volume_id] == mv)) { vol->is_active = visible; + if (!vol->is_modifier) + vol->color.a(1.f); if (instance_idx == -1) { vol->force_native_color = false; @@ -1390,9 +1392,13 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject const GLGizmosManager& gm = get_gizmos_manager(); auto gizmo_type = gm.get_current_type(); if ( (gizmo_type == GLGizmosManager::FdmSupports - || gizmo_type == GLGizmosManager::Seam) - && ! vol->is_modifier) + || gizmo_type == GLGizmosManager::Seam + || gizmo_type == GLGizmosManager::Cut) + && ! vol->is_modifier) { vol->force_neutral_color = true; + if (gizmo_type == GLGizmosManager::Cut) + vol->color.a(0.95f); + } else if (gizmo_type == GLGizmosManager::MmuSegmentation) vol->is_active = false; else diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a5b2acb32..b761deaa6 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -855,6 +855,7 @@ public: Linef3 mouse_ray(const Point& mouse_pos); bool is_mouse_dragging() const { return m_mouse.dragging; } + void set_mouse_as_dragging() { m_mouse.dragging = true; } double get_size_proportional_to_max_bed_size(double factor) const; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 2c1ee7a6c..3b5af8cd9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -11,6 +11,7 @@ #include "GalleryDialog.hpp" #include "MainFrame.hpp" #include "slic3r/Utils/UndoRedo.hpp" +#include "Gizmos/GLGizmoCut.hpp" #include "OptionsGroup.hpp" #include "Tab.hpp" @@ -2579,6 +2580,10 @@ void ObjectList::part_selection_changed() GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); if (gizmos_mgr.get_current_type() != gizmo_type) gizmos_mgr.open_gizmo(gizmo_type); + if (info_type == InfoItemType::Cut) { + GLGizmoCut3D* cut = dynamic_cast(gizmos_mgr.get_current()); + cut->set_connectors_editing(); + } break; } case InfoItemType::Sinking: { break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 6ddb804d4..ced04197d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -318,6 +318,9 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { on_start_dragging(); + // prevent change of hover_id during dragging + m_parent.set_mouse_as_dragging(); + // Let the plater know that the dragging started m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); m_parent.set_as_dirty(); @@ -327,6 +330,7 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { // when mouse cursor leave window than finish actual dragging operation bool is_leaving = mouse_event.Leaving(); if (mouse_event.Dragging()) { + m_parent.set_mouse_as_dragging(); Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); auto ray = m_parent.mouse_ray(mouse_coord); UpdateData data(ray, mouse_coord); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 972598b73..8cf7ab42f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -302,6 +302,30 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i return old_val != value; } +bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double& value_in) +{ + ImGui::AlignTextToFramePadding(); + m_imgui->text(label); + ImGui::SameLine(m_label_width); + ImGui::PushItemWidth(m_control_width); + + float value = (float)value_in; + if (m_imperial_units) + value *= ObjectManipulation::mm_to_in; + float old_val = value; + + const BoundingBoxf3 bbox = bounding_box(); + const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); + + m_imgui->slider_float(("##" + label).c_str(), &value, 1.0f, mean_size); + + ImGui::SameLine(); + m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + + value_in = (double)(value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0)); + return old_val != value; +} + void GLGizmoCut3D::render_move_center_input(int axis) { ImGui::AlignTextToFramePadding(); @@ -511,23 +535,26 @@ void GLGizmoCut3D::render_cut_center_graber() const Transform3d view_matrix = camera.get_view_matrix() * graber.matrix * Geometry::assemble_transform(center, angles); - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones()); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_sphere.render(); - const BoundingBoxf3 tbb = transformed_bounding_box(); - if (tbb.max.z() >= 0.0) { - view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), scale); + + if (tbb.min.z() <= 0.0) { + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), scale); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); m_cone.render(); } - if (tbb.min.z() <= 0.0) { - view_model_matrix = view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), scale); + { + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones()); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_sphere.render(); + } + + if (tbb.max.z() >= 0.0) { + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), scale); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); @@ -892,7 +919,7 @@ void GLGizmoCut3D::on_render() render_cut_center_graber(); render_cut_plane(); if (m_hover_id < m_group_id && m_mode == size_t(CutMode::cutPlanar) && !cut_line_processing()) - m_rotation_gizmo.render(); + m_rotation_gizmo.render(); } render_cut_line(); @@ -950,13 +977,25 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; double bottom = (tbb.max.z() <= 0.0 ? tbb.size().z() : (tbb.min.z() * (-1))) * koef; + Vec3d tbb_sz = tbb.size(); + wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + + ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + + ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build size")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); + //static float v = 0.; // TODO: connect to cutting plane position m_imgui->text(_L("Cut position: ")); render_move_center_input(Z); //m_imgui->input_double(unit_str, v); //v = std::clamp(v, 0.f, float(bottom+top)); if (m_imgui->button("Reset cutting plane")) { - // TODO: reset both position and rotation + set_center(bounding_box().center()); + m_rotation_gizmo.set_rotation(Vec3d::Zero()); + update_clipper(); } ////// @@ -1015,7 +1054,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(); m_imgui->disabled_begin(!m_keep_upper); m_imgui->disabled_begin(is_approx(m_rotation_gizmo.get_rotation().x(), 0.) && is_approx(m_rotation_gizmo.get_rotation().y(), 0.)); - m_imgui->checkbox(_L("Place on cut") + "##upper", m_rotate_upper); + m_imgui->checkbox(_L("Place on cut") + "##upper", m_rotate_upper); // #ysTODO implement place on cut instead of Flip? m_imgui->disabled_end(); m_imgui->disabled_end(); @@ -1029,7 +1068,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->disabled_end(); ImGui::SameLine(); m_imgui->disabled_begin(!m_keep_lower); - m_imgui->checkbox(_L("Place on cut") + "##lower", m_rotate_lower); + m_imgui->checkbox(_L("Place on cut") + "##lower", m_rotate_lower); // #ysTODO implement place on cut instead of Flip? m_imgui->disabled_end(); } @@ -1064,17 +1103,19 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) update_connector_shape(); - if (render_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) + if (render_slicer_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) for (auto& connector : connectors) connector.height = float(m_connector_depth_ratio); - if (render_double_input(_u8L("Size"), m_connector_size)) + if (render_slicer_double_input(_u8L("Size"), m_connector_size)) for (auto& connector : connectors) connector.radius = float(m_connector_size * 0.5); m_imgui->disabled_end(); - if (m_imgui->button(_L("Confirm connectors"))) + if (m_imgui->button(_L("Confirm connectors"))) { + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_connectors_editing = false; + } } ImGui::Separator(); @@ -1111,7 +1152,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) } } -// get volume transformation regarding to the "border". Border is related from the siae of connectors +// get volume transformation regarding to the "border". Border is related from the size of connectors Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const { bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm); @@ -1141,10 +1182,7 @@ void GLGizmoCut3D::render_connectors(bool picking) if (picking && ! m_connectors_editing) return; - const bool depth_test = m_connectors_editing; - if (! depth_test) - ::glDisable(GL_DEPTH_TEST); - Slic3r::ScopeGuard guard_depth_test([&](){ if (! depth_test) ::glEnable(GL_DEPTH_TEST); }); + ::glEnable(GL_DEPTH_TEST); if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) return; @@ -1225,7 +1263,7 @@ void GLGizmoCut3D::render_connectors(bool picking) const Transform3d volume_trafo = get_volume_transformation(mv); if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { - render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(0.5f, 0.5f, 0.5f, 1.f); + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : /*ColorRGBA(0.5f, 0.5f, 0.5f, 1.f)*/ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); break; } render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index dfaef3fb2..76ec2f7ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -124,6 +124,7 @@ public: void put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset); void update_clipper(); void update_clipper_on_render(); + void set_connectors_editing() { m_connectors_editing = true; } BoundingBoxf3 bounding_box() const; BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const; @@ -153,6 +154,7 @@ private: void set_center(const Vec3d& center); bool render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); bool render_double_input(const std::string& label, double& value_in); + bool render_slicer_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); void render_rotation_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 9e2f9e08b..d82fbd587 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -301,13 +301,13 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); if (coordinates_type == ECoordinatesType::World) { m_bounding_box = selection.get_bounding_box(); - m_center = m_bounding_box.center(); + m_center = m_has_forced_center ? m_forced_center : m_bounding_box.center(); } else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { const GLVolume& v = *selection.get_first_volume(); m_bounding_box = v.transformed_convex_hull_bounding_box( v.get_instance_transformation().get_scaling_factor_matrix() * v.get_volume_transformation().get_scaling_factor_matrix()); - m_center = v.world_matrix() * m_bounding_box.center(); + m_center = v.world_matrix() * (m_has_forced_center ? m_forced_center : m_bounding_box.center()); } else { m_bounding_box.reset(); @@ -318,7 +318,7 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) } const Geometry::Transformation inst_trafo = selection.get_first_volume()->get_instance_transformation(); m_bounding_box = m_bounding_box.transformed(inst_trafo.get_scaling_factor_matrix()); - m_center = inst_trafo.get_matrix_no_scaling_factor() * m_bounding_box.center(); + m_center = inst_trafo.get_matrix_no_scaling_factor() * (m_has_forced_center ? m_forced_center : m_bounding_box.center()); } m_radius = Offset + m_bounding_box.radius(); From 15418bfb76be3830b9352b4a7d29dae5d1dd1950 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 18 Jul 2022 16:11:54 +0200 Subject: [PATCH 48/93] Cut WIP: Extensions for grabbers + Some code refactoring (RotationGizmo isn't used anymore) + A reversion from https://github.com/prusa3d/PrusaSlicer/commit/63890b5f8d352d3ef1228fa00b0d3932717f933d is putted back --- src/slic3r/GUI/GLCanvas3D.hpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 4 - src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 461 +++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 14 +- 4 files changed, 279 insertions(+), 201 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b761deaa6..a5b2acb32 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -855,7 +855,6 @@ public: Linef3 mouse_ray(const Point& mouse_pos); bool is_mouse_dragging() const { return m_mouse.dragging; } - void set_mouse_as_dragging() { m_mouse.dragging = true; } double get_size_proportional_to_max_bed_size(double factor) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index ced04197d..6ddb804d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -318,9 +318,6 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { on_start_dragging(); - // prevent change of hover_id during dragging - m_parent.set_mouse_as_dragging(); - // Let the plater know that the dragging started m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); m_parent.set_as_dirty(); @@ -330,7 +327,6 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { // when mouse cursor leave window than finish actual dragging operation bool is_leaving = mouse_event.Leaving(); if (mouse_event.Dragging()) { - m_parent.set_mouse_as_dragging(); Point mouse_coord(mouse_event.GetX(), mouse_event.GetY()); auto ray = m_parent.mouse_ray(mouse_coord); UpdateData data(ray, mouse_coord); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 8cf7ab42f..5ea85964c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -27,6 +27,29 @@ namespace GUI { static const double Margin = 20.0; static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); +const unsigned int ScaleStepsCount = 72; +const float ScaleLongTooth = 0.1f; // in percent of radius +const unsigned int SnapRegionsCount = 8; + +// Generates mesh for a line +GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) +{ + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + Vec3f start = Vec3f::Zero(); + init_data.add_vertex(beg_pos); + Vec3f stop = Vec3f::UnitZ(); + init_data.add_vertex(end_pos); + + // indices + init_data.add_line(0, 1); + return init_data; +} + #define use_grabber_extension 1 GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) @@ -34,13 +57,9 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, , m_connector_type (CutConnectorType::Plug) , m_connector_style (size_t(CutConnectorStyle::Prizm)) , m_connector_shape_id (size_t(CutConnectorShape::Hexagon)) - , m_rotation_gizmo(GLGizmoRotate3D(parent, "", -1)) , m_rotation_matrix(Slic3r::Matrix3d::Identity()) + , m_connectors_group_id (3) { - m_rotation_gizmo.use_only_grabbers(); - m_group_id = 3; - m_connectors_group_id = 4; - m_modes = { _u8L("Planar")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") }; @@ -63,9 +82,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, std::string GLGizmoCut3D::get_tooltip() const { - std::string tooltip = m_rotation_gizmo.get_tooltip(); - if (tooltip.empty() && - (m_hover_id == m_group_id || m_grabbers[0].dragging)) { + std::string tooltip; + if (m_hover_id == Z) { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); const BoundingBoxf3 tbb = transformed_bounding_box(); @@ -81,6 +99,10 @@ std::string GLGizmoCut3D::get_tooltip() const } return tooltip; } + if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { + std::string axis = m_hover_id == X ? "X" : "Y"; + return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); + } return tooltip; } @@ -88,14 +110,7 @@ std::string GLGizmoCut3D::get_tooltip() const bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { if (mouse_event.Moving() && !cut_line_processing()) - return false; - - if (m_rotation_gizmo.on_mouse(mouse_event)) { - if (mouse_event.LeftUp()) - on_stop_dragging(); - update_clipper(); - return true; - } + return false; if (use_grabbers(mouse_event)) return true; @@ -203,7 +218,7 @@ void GLGizmoCut3D::put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_ void GLGizmoCut3D::update_clipper() { - const Vec3d& angles = m_rotation_gizmo.get_rotation(); + const Vec3d angles = Geometry::Transformation(m_rotation_m).get_rotation(); BoundingBoxf3 box = bounding_box(); double radius = box.radius(); @@ -239,7 +254,6 @@ void GLGizmoCut3D::update_clipper_on_render() void GLGizmoCut3D::set_center(const Vec3d& center) { set_center_pos(center); - m_rotation_gizmo.set_center(m_plane_center); update_clipper(); } @@ -348,27 +362,6 @@ void GLGizmoCut3D::render_move_center_input(int axis) } } -void GLGizmoCut3D::render_rotation_input(int axis) -{ - m_imgui->text(m_axis_names[axis] + ":"); - ImGui::SameLine(); - - Vec3d rotation = m_rotation_gizmo.get_rotation(); - double value = Geometry::rad2deg(rotation[axis]); - if (value > 360) - value -= 360; - - ImGui::PushItemWidth(0.3*m_control_width); - ImGui::InputDouble(("##rotate_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); - ImGui::SameLine(); - - if (double val = Geometry::deg2rad(value); val != rotation[axis]) { - rotation[axis] = val; - m_rotation_gizmo.set_rotation(rotation); - update_clipper(); - } -} - void GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type) { ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 2*m_label_width); @@ -433,7 +426,7 @@ void GLGizmoCut3D::render_cut_plane() const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( m_plane_center, - m_rotation_gizmo.get_rotation(), + Geometry::Transformation(m_rotation_m).get_rotation(), Vec3d::Ones(), Vec3d::Ones() ); @@ -495,50 +488,61 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } -void GLGizmoCut3D::render_cut_center_graber() +void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) { - ::glDisable(GL_DEPTH_TEST); - Slic3r::ScopeGuard guard([]() { ::glEnable(GL_DEPTH_TEST); }); - const Vec3d& angles = m_rotation_gizmo.get_rotation(); - m_grabbers[0].angles = angles; - m_grabbers[0].color = GRABBER_COLOR; + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); -#if use_grabber_extension - // UI experiments with grabber - - m_grabbers[0].center = m_plane_center; - - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); if (!shader) return; - auto color = m_hover_id == m_group_id ? complementary(GRABBER_COLOR) : GRABBER_COLOR; + glsafe(::glLineWidth(m_hover_id == X || m_hover_id == Y ? 3.0f : 1.0f)); + + ColorRGBA color = picking ? picking_decode(BASE_ID - Z) : + m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; + m_sphere.set_color(color); m_cone.set_color(color); const Camera& camera = wxGetApp().plater()->get_camera(); const Grabber& graber = m_grabbers.front(); - const Vec3d& center = graber.center; const BoundingBoxf3 box = bounding_box(); const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 6.0); - const double size = m_dragging ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + double size = m_dragging && m_hover_id == Z ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); - const Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - const Vec3d offset = 1.25 * size * Vec3d::UnitZ(); + Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + Vec3d offset = 1.25 * size * Vec3d::UnitZ(); shader->start_using(); shader->set_uniform("emission_factor", 0.1f); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - const Transform3d view_matrix = camera.get_view_matrix() * graber.matrix * - Geometry::assemble_transform(center, angles); + const Transform3d view_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_rotation_m; const BoundingBoxf3 tbb = transformed_bounding_box(); + const double grabber_connection_len = std::min(0.75 * m_radius, 35.0) ; - if (tbb.min.z() <= 0.0) { - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), scale); + auto render_grabber_connection = [shader, view_matrix, camera, grabber_connection_len, this](bool render, const ColorRGBA& color) + { + if (!render) + return; + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, grabber_connection_len)); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + m_grabber_connection.set_color(color); + m_grabber_connection.render(); + }; + + // render Z grabber + + render_grabber_connection((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y, color); + + if ((!m_dragging && m_hover_id < 0 || m_hover_id == Z) && tbb.min.z() <= 0.0) { + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); @@ -553,8 +557,84 @@ void GLGizmoCut3D::render_cut_center_graber() m_sphere.render(); } - if (tbb.max.z() >= 0.0) { - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), scale); + if ((!m_dragging && m_hover_id < 0 || m_hover_id == Z) && tbb.max.z() >= 0.0) { + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), cone_scale); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_cone.render(); + } + + // render top sphere for X/Y grabbers + + offset = Vec3d(0.0, 0.0, grabber_connection_len); + size = m_dragging && (m_hover_id == X || m_hover_id == Y) ? + double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + if ((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y) + { + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), size * Vec3d::Ones()); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_sphere.set_color(m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : + m_hover_id == X ? complementary(ColorRGBA::RED() ) : ColorRGBA::GRAY()); + m_sphere.render(); + } + + // render Y grabber + + size = m_dragging && m_hover_id == Y ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y) + { + if (picking) + color = picking_decode(BASE_ID - Y); + else + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); + + render_grabber_connection(m_hover_id == Y, color); + + m_cone.set_color(color); + + offset = Vec3d(1.25 * size, 0.0, grabber_connection_len); + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_cone.render(); + + offset = Vec3d(-1.25 * size, 0.0, grabber_connection_len); + view_model_matrix = view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_cone.render(); + } + + // render X grabber + + size = m_dragging && m_hover_id == X ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + if ((!m_dragging && m_hover_id < 0) || m_hover_id == X) + { + if (picking) + color = picking_decode(BASE_ID - X); + else + color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); + + render_grabber_connection(m_hover_id == X, color); + + m_cone.set_color(color); + + offset = Vec3d(0.0, 1.25 * size, grabber_connection_len); + Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + m_cone.render(); + + offset = Vec3d(0.0, -1.25 * size, grabber_connection_len); + view_model_matrix = view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); @@ -562,68 +642,6 @@ void GLGizmoCut3D::render_cut_center_graber() } shader->stop_using(); - -#else - const BoundingBoxf3 box = bounding_box(); - - Vec3d grabber_center = m_plane_center; - grabber_center[Z] += float(box.radius()/2.0); // Margin - rotate_vec3d_around_center(grabber_center, angles, m_plane_center); - - m_grabbers[0].center = grabber_center; - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glLineWidth(m_hover_id == m_group_id ? 2.0f : 1.5f)); - - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); -#if ENABLE_GL_SHADERS_ATTRIBUTES - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix()); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - m_grabber_connection.reset(); - - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA::YELLOW(); - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex((Vec3f)m_plane_center.cast()); - init_data.add_vertex((Vec3f)m_grabbers[0].center.cast()); - - // indices - init_data.add_line(0, 1); - - m_grabber_connection.init_from(std::move(init_data)); - - m_grabber_connection.render(); - - shader->stop_using(); - } - -#if !ENABLE_LEGACY_OPENGL_REMOVAL - glsafe(::glColor3f(1.0, 1.0, 0.0)); - ::glBegin(GL_LINES); - ::glVertex3dv(plane_center.data()); - ::glVertex3dv(m_grabbers[0].center.data()); - glsafe(::glEnd()); -#endif // !ENABLE_LEGACY_OPENGL_REMOVAL - - shader = wxGetApp().get_shader("gouraud_light"); - if (shader != nullptr) { - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - - m_grabbers[0].render(m_hover_id == m_group_id, float((box.size().x() + box.size().y() + box.size().z()) / 3.0)); - - shader->stop_using(); - } -#endif } void GLGizmoCut3D::render_cut_line() @@ -644,21 +662,9 @@ void GLGizmoCut3D::render_cut_line() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #endif // ENABLE_GL_SHADERS_ATTRIBUTES m_cut_line.reset(); + m_cut_line.init_from(its_make_line((Vec3f)m_line_beg.cast(), (Vec3f)m_line_end.cast())); - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = GRABBER_COLOR; - init_data.reserve_vertices(2); - init_data.reserve_indices(2); - - // vertices - init_data.add_vertex((Vec3f)m_line_beg.cast()); - init_data.add_vertex((Vec3f)m_line_end.cast()); - - // indices - init_data.add_line(0, 1); - - m_cut_line.init_from(std::move(init_data)); + m_cut_line.set_color(GRABBER_COLOR); m_cut_line.render(); shader->stop_using(); @@ -670,9 +676,6 @@ bool GLGizmoCut3D::on_init() m_grabbers.emplace_back(); m_shortcut_key = WXK_CONTROL_C; - if(!m_rotation_gizmo.init()) - return false; - return true; } @@ -683,8 +686,8 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) m_ar_plane_center, m_ar_rotations); set_center_pos(m_ar_plane_center, true); - m_rotation_gizmo.set_center(m_ar_plane_center); - m_rotation_gizmo.set_rotation(m_ar_rotations); + m_rotation_m = Geometry::rotation_transform(m_ar_rotations); + force_update_clipper_on_render = true; m_parent.request_extra_frame(); @@ -709,22 +712,18 @@ void GLGizmoCut3D::on_set_state() // initiate archived values m_ar_plane_center = m_plane_center; - m_ar_rotations = m_rotation_gizmo.get_rotation(); + m_ar_rotations = Geometry::Transformation(m_rotation_m).get_rotation(); m_parent.request_extra_frame(); } else m_c->object_clipper()->release(); - m_rotation_gizmo.set_center(m_plane_center); - m_rotation_gizmo.set_state(m_state); - force_update_clipper_on_render = m_state == On; } void GLGizmoCut3D::on_set_hover_id() { - m_rotation_gizmo.set_hover_id(m_hover_id < m_group_id ? m_hover_id: -1); } bool GLGizmoCut3D::on_is_activable() const @@ -734,21 +733,56 @@ bool GLGizmoCut3D::on_is_activable() const return m_parent.get_selection().is_single_full_instance(); } +Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const +{ + double half_pi = 0.5 * double(PI); + + Transform3d m = Transform3d::Identity(); + + switch (axis) + { + case X: + { + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); + m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY())); + break; + } + case Y: + { + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitY())); + m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitZ())); + break; + } + default: + case Z: + { + // no rotation applied + break; + } + } + + m = m * m_rotation_m.inverse(); + + m.translate(-m_plane_center); + + return transform(mouse_ray, m).intersect_plane(0.0); +} + void GLGizmoCut3D::on_dragging(const UpdateData& data) { - if (m_hover_id < m_group_id) + if (m_hover_id < 0) return; CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - if (m_hover_id == m_group_id) { + if (m_hover_id == Z) { #if use_grabber_extension Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ();// some Margin - rotate_vec3d_around_center(starting_box_center, m_rotation_gizmo.get_rotation(), m_plane_center); + rotate_vec3d_around_center(starting_box_center, Geometry::Transformation(m_rotation_m).get_rotation(), m_plane_center); #else const Vec3d& starting_box_center = m_plane_center; #endif - const Vec3d& starting_drag_position = m_grabbers[0].center; + const Vec3d& starting_drag_position = m_plane_center; double projection = 0.0; Vec3d starting_vec = starting_drag_position - starting_box_center; @@ -775,7 +809,49 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) set_center(m_plane_center + shift); } - else if (m_hover_id > m_group_id && m_connector_mode == CutConnectorMode::Manual) + else if (m_hover_id == X || m_hover_id == Y) { + + Vec3d rotation = Vec3d::Zero(); + + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray)); + + const Vec2d orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); + + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); + if (cross2(orig_dir, new_dir) < 0.0) + theta = 2.0 * (double)PI - theta; + + const double len = mouse_pos.norm(); + + // snap to coarse snap region + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = 2.0 * double(PI) / double(SnapRegionsCount); + theta = step * std::round(theta / step); + } + else { + // snap to fine snap region (scale) + if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = 2.0 * double(PI) / double(ScaleStepsCount); + theta = step * std::round(theta / step); + } + } + + if (theta == 2.0 * double(PI)) + theta = 0.0; + + if (m_hover_id == X) + theta += 0.5 * double(PI); + + rotation[m_hover_id] = theta; + + m_rotation_m = m_rotation_m * Geometry::rotation_transform(rotation); + + update_clipper(); + } + + + else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) { std::pair pos_and_normal; if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) @@ -786,17 +862,17 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) void GLGizmoCut3D::on_start_dragging() { - if (m_hover_id > m_group_id && m_connector_mode == CutConnectorMode::Manual) + if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move connector"), UndoRedo::SnapshotType::GizmoAction); } void GLGizmoCut3D::on_stop_dragging() { - if (m_hover_id < m_group_id) { + if (m_hover_id == X || m_hover_id == Y) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); - m_ar_rotations = m_rotation_gizmo.get_rotation(); + m_ar_rotations = Geometry::Transformation(m_rotation_m).get_rotation(); } - else if (m_hover_id == m_group_id) { + else if (m_hover_id == Z) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); m_ar_plane_center = m_plane_center; } @@ -841,7 +917,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= m_c->selection_info()->get_sla_shift(); - const Vec3d& rotation = m_rotation_gizmo.get_rotation(); + const Vec3d rotation = Geometry::Transformation(m_rotation_m).get_rotation(); const auto move = Geometry::assemble_transform(-cut_center_offset); const auto rot_z = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, 0, -rotation.z())); const auto rot_y = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, -rotation.y(), 0)); @@ -879,9 +955,16 @@ bool GLGizmoCut3D::update_bb() m_bb_center = box.center(); set_center_pos(m_bb_center + m_center_offset, true); + m_radius = box.radius(); + m_snap_coarse_in_radius = m_radius / 3.0f; + m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_fine_in_radius = m_radius; + m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; + m_plane.reset(); m_cone.reset(); m_sphere.reset(); + m_grabber_connection.reset(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { m_selected.clear(); m_selected.resize(selection->model_object()->cut_connectors.size(), false); @@ -894,8 +977,8 @@ bool GLGizmoCut3D::update_bb() void GLGizmoCut3D::on_render() { - if (update_bb()) { - m_rotation_gizmo.set_center(m_plane_center); + bool updated_bb = update_bb(); + if (updated_bb) { update_clipper_on_render(); } @@ -903,6 +986,8 @@ void GLGizmoCut3D::on_render() m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); if (!m_sphere.is_initialized()) m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); + if (!m_grabber_connection.is_initialized()) + m_grabber_connection.init_from(its_make_line(Vec3f::Zero(), Vec3f::UnitZ())); if (force_update_clipper_on_render) update_clipper_on_render(); @@ -915,11 +1000,9 @@ void GLGizmoCut3D::on_render() if (! m_connectors_editing) ::glEnable(GL_DEPTH_TEST); - if (!m_hide_cut_plane && ! m_connectors_editing) { - render_cut_center_graber(); + if (!m_hide_cut_plane && !m_connectors_editing) { render_cut_plane(); - if (m_hover_id < m_group_id && m_mode == size_t(CutMode::cutPlanar) && !cut_line_processing()) - m_rotation_gizmo.render(); + render_cut_center_graber(); } render_cut_line(); @@ -927,9 +1010,7 @@ void GLGizmoCut3D::on_render() void GLGizmoCut3D::on_render_for_picking() { - m_rotation_gizmo.render_for_picking(); - render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); - + render_cut_center_graber(true); render_connectors(true); } @@ -994,7 +1075,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) //v = std::clamp(v, 0.f, float(bottom+top)); if (m_imgui->button("Reset cutting plane")) { set_center(bounding_box().center()); - m_rotation_gizmo.set_rotation(Vec3d::Zero()); + m_rotation_m = Transform3d::Identity(); update_clipper(); } ////// @@ -1010,17 +1091,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) // render_move_center_input(axis); // m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); - // ImGui::AlignTextToFramePadding(); - // m_imgui->text(_L("Rotation")); - - // m_imgui->disabled_begin(m_rotation_gizmo.get_rotation() == Vec3d::Zero()); - // revert_rotation = render_revert_button("rotation"); - // m_imgui->disabled_end(); - - // for (Axis axis : {X, Y, Z}) - // render_rotation_input(axis); - // m_imgui->text(_L("°")); - // ImGui::Separator(); // double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; @@ -1053,7 +1123,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->disabled_end(); ImGui::SameLine(); m_imgui->disabled_begin(!m_keep_upper); - m_imgui->disabled_begin(is_approx(m_rotation_gizmo.get_rotation().x(), 0.) && is_approx(m_rotation_gizmo.get_rotation().y(), 0.)); + m_imgui->disabled_begin(is_approx(Geometry::Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Geometry::Transformation(m_rotation_m).get_rotation().y(), 0.)); m_imgui->checkbox(_L("Place on cut") + "##upper", m_rotate_upper); // #ysTODO implement place on cut instead of Flip? m_imgui->disabled_end(); m_imgui->disabled_end(); @@ -1116,14 +1186,17 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_connectors_editing = false; } + m_parent.request_extra_frame(); } ImGui::Separator(); m_imgui->text(m_has_invalid_connector ? wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.") : wxString()); - m_imgui->disabled_begin(!can_perform_cut()); - cut_clicked = m_imgui->button(_L("Perform cut")); - m_imgui->disabled_end(); + if (!m_connectors_editing) { + m_imgui->disabled_begin(!can_perform_cut()); + cut_clicked = m_imgui->button(_L("Perform cut")); + m_imgui->disabled_end(); + } m_imgui->end(); @@ -1147,7 +1220,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (revert_move) set_center(bounding_box().center()); if (revert_rotation) { - m_rotation_gizmo.set_rotation(Vec3d::Zero()); + m_rotation_m = Transform3d::Identity(); update_clipper(); } } @@ -1158,7 +1231,7 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm); const Transform3d connector_trafo = Geometry::assemble_transform( is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(), - m_rotation_gizmo.get_rotation(), + Geometry::Transformation(m_rotation_m).get_rotation(), Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio), Vec3d::Ones()); const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size(); @@ -1277,7 +1350,7 @@ void GLGizmoCut3D::render_connectors(bool picking) const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( Vec3d(pos.x(), pos.y(), pos.z()), - m_rotation_gizmo.get_rotation(), + Geometry::Transformation(m_rotation_m).get_rotation(), Vec3d(connector.radius, connector.radius, height), Vec3d::Ones() ); @@ -1312,7 +1385,7 @@ void GLGizmoCut3D::render_connectors(bool picking) bool GLGizmoCut3D::can_perform_cut() const { - if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower)) + if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; const BoundingBoxf3 tbb = transformed_bounding_box(true); @@ -1343,13 +1416,15 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) if(!mo) return; + Vec3d rotation = Geometry::Transformation(m_rotation_m).get_rotation(); + const bool has_connectors = !mo->cut_connectors.empty(); { // update connectors pos as offset of its center before cut performing if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); for (CutConnector& connector : mo->cut_connectors) { - connector.rotation = m_rotation_gizmo.get_rotation(); + connector.rotation = rotation; if (m_connector_type == CutConnectorType::Dowel) { if (m_connector_style == size_t(CutConnectorStyle::Prizm)) @@ -1359,7 +1434,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // culculate shift of the connector center regarding to the position on the cut plane #if use_grabber_extension Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); - rotate_vec3d_around_center(shifted_center, m_rotation_gizmo.get_rotation(), m_plane_center); + rotate_vec3d_around_center(shifted_center, rotation, m_plane_center); Vec3d norm = (shifted_center - m_plane_center).normalized(); #else Vec3d norm = (m_grabbers[0].center - m_plane_center).normalize(); @@ -1373,7 +1448,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } } - plater->cut(object_idx, instance_idx, cut_center_offset, m_rotation_gizmo.get_rotation(), + plater->cut(object_idx, instance_idx, cut_center_offset, rotation, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | @@ -1500,7 +1575,7 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse Transform3d m = Transform3d::Identity(); m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), cross_dir).toRotationMatrix(); - m_rotation_gizmo.set_rotation(Geometry::Transformation(m).get_rotation()); + m_rotation_m = m; set_center(m_plane_center + cross_dir * (cross_dir.dot(pt - m_plane_center))); @@ -1537,7 +1612,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi //std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, m_rotation_gizmo.get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); update_model_object(); m_selected.push_back(false); assert(m_selected.size() == connectors.size()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 76ec2f7ba..ea053d99f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -20,7 +20,7 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoCut3D : public GLGizmoBase { - GLGizmoRotate3D m_rotation_gizmo; + Transform3d m_rotation_m{ Transform3d::Identity() }; double m_snap_step{ 1.0 }; int m_connectors_group_id; @@ -35,6 +35,13 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; + // values from RotationGizmo + float m_radius{ 0.0f }; + float m_snap_coarse_in_radius{ 0.0f }; + float m_snap_coarse_out_radius{ 0.0f }; + float m_snap_fine_in_radius{ 0.0f }; + float m_snap_fine_out_radius{ 0.0f }; + GLModel m_connector_shape; TriangleMesh m_connector_mesh; // workaround for using of the clipping plane normal @@ -45,6 +52,7 @@ class GLGizmoCut3D : public GLGizmoBase #if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; + GLModel m_grabber_connection; GLModel m_cut_line; GLModel m_cone; GLModel m_sphere; @@ -138,6 +146,7 @@ protected: CommonGizmosDataID on_get_requirements() const override; void on_set_hover_id() override; bool on_is_activable() const override; + Vec3d mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const; void on_dragging(const UpdateData& data) override; void on_start_dragging() override; void on_stop_dragging() override; @@ -156,7 +165,6 @@ private: bool render_double_input(const std::string& label, double& value_in); bool render_slicer_double_input(const std::string& label, double& value_in); void render_move_center_input(int axis); - void render_rotation_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); void render_connect_type_radio_button(CutConnectorType type); @@ -167,7 +175,7 @@ private: bool cut_line_processing() const; void render_cut_plane(); - void render_cut_center_graber(); + void render_cut_center_graber(bool picking = false); void render_cut_line(); void perform_cut(const Selection& selection); void set_center_pos(const Vec3d& center_pos, bool force = false); From cd8e0d002ba30cd12c107d421aaeb128407a8f1f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 19 Jul 2022 16:58:27 +0200 Subject: [PATCH 49/93] Cut WIP: Added "Place o cut" --- src/libslic3r/Model.cpp | 38 +++++++++++++++------------- src/libslic3r/Model.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 24 +++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 ++ 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 19b2f8f7d..36034d904 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1494,19 +1494,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const instances[instance]->get_mirror() ); - const auto cut_matrix = Geometry::assemble_transform( - -cut_center, - Vec3d::Zero(), - Vec3d::Ones(), - Vec3d::Ones() - ); + const auto cut_matrix = Geometry::assemble_transform(-cut_center); - const auto invert_cut_matrix = Geometry::assemble_transform( - cut_center, - cut_rotation, - Vec3d::Ones(), - Vec3d::Ones() - ); + const auto invert_cut_matrix = Geometry::assemble_transform(cut_center, cut_rotation); // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); @@ -1522,11 +1512,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (!volume->is_model_part()) { if (volume->source.is_connector) { if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + + Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper) ? + Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix : + instance_matrix * volume_matrix; + volume->set_transformation(m); + ModelVolume* vol = upper->add_volume(*volume); // make a "hole" dipper vol->set_scaling_factor(Z, 1.1 * vol->get_scaling_factor(Z)); } if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + + Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? + Geometry::rotation_transform(Geometry::deg2rad(180.0) * Vec3d::UnitX()) * Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix : + instance_matrix * volume_matrix; + volume->set_transformation(m); ModelVolume* vol = lower->add_volume(*volume); if (attributes.has(ModelObjectCutAttribute::CreateDowels)) // make a "hole" dipper @@ -1567,10 +1568,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); - mesh.transform(cut_matrix * instance_matrix * volume_matrix, true); - mesh.rotate(-cut_rotation.z(), Z); - mesh.rotate(-cut_rotation.y(), Y); - mesh.rotate(-cut_rotation.x(), X); + mesh.transform(Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix, true); volume->reset_mesh(); // Reset volume transformation except for offset @@ -1590,7 +1588,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const } if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { - upper_mesh.transform(invert_cut_matrix); + if (!attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) + upper_mesh.transform(invert_cut_matrix); ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; @@ -1601,7 +1600,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const vol->set_material(volume->material_id(), *volume->material()); } if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - lower_mesh.transform(invert_cut_matrix); + if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) + lower_mesh.transform(Geometry::assemble_transform(Vec3d::Zero(), Geometry::deg2rad(180.0)*Vec3d::UnitX())); + else + lower_mesh.transform(invert_cut_matrix); ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 72d9b33c1..6afa2efe8 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -304,7 +304,7 @@ enum class ModelVolumeType : int { SUPPORT_ENFORCER, }; -enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, CreateDowels }; +enum class ModelObjectCutAttribute : int { KeepUpper, KeepLower, FlipUpper, FlipLower, PlaceOnCutUpper, PlaceOnCutLower, CreateDowels }; using ModelObjectCutAttributes = enum_bitmask; ENABLE_ENUM_BITMASK_OPERATORS(ModelObjectCutAttribute); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5ea85964c..0d8eae28d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1121,10 +1121,17 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->disabled_begin(!connectors.empty()); m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); m_imgui->disabled_end(); - ImGui::SameLine(); + ImGui::SameLine(3 * m_label_width); + m_imgui->disabled_begin(!m_keep_upper); m_imgui->disabled_begin(is_approx(Geometry::Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Geometry::Transformation(m_rotation_m).get_rotation().y(), 0.)); - m_imgui->checkbox(_L("Place on cut") + "##upper", m_rotate_upper); // #ysTODO implement place on cut instead of Flip? + + if (m_imgui->checkbox(_L("Place on cut") + "##upper", m_place_on_cut_upper)) + m_rotate_upper = false; + ImGui::SameLine(); + if (m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper)) + m_place_on_cut_upper = false; + m_imgui->disabled_end(); m_imgui->disabled_end(); @@ -1134,11 +1141,18 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(2 * m_label_width); m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); m_imgui->disabled_end(); - ImGui::SameLine(); + ImGui::SameLine(3 * m_label_width); m_imgui->disabled_begin(!m_keep_lower); - m_imgui->checkbox(_L("Place on cut") + "##lower", m_rotate_lower); // #ysTODO implement place on cut instead of Flip? + + if (m_imgui->checkbox(_L("Place on cut") + "##lower", m_place_on_cut_lower)) + m_rotate_lower = false; + ImGui::SameLine(); + if (m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower)) + m_place_on_cut_lower = false; + m_imgui->disabled_end(); } @@ -1451,6 +1465,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) plater->cut(object_idx, instance_idx, cut_center_offset, rotation, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | + only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ea053d99f..dbb1e6ae0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -61,6 +61,8 @@ class GLGizmoCut3D : public GLGizmoBase bool m_keep_upper{ true }; bool m_keep_lower{ true }; + bool m_place_on_cut_upper{ true }; + bool m_place_on_cut_lower{ true }; bool m_rotate_upper{ false }; bool m_rotate_lower{ false }; From 003acee218ce62869b79c1a80f958ac86925d398 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 20 Jul 2022 16:36:36 +0200 Subject: [PATCH 50/93] Cut WIP: Added snapping for the rotation of cut plane + Some code refactoring --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 376 ++++++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 19 +- 2 files changed, 243 insertions(+), 152 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 0d8eae28d..5fd0b28d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -27,12 +27,15 @@ namespace GUI { static const double Margin = 20.0; static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); +const unsigned int AngleResolution = 64; const unsigned int ScaleStepsCount = 72; +const float ScaleStepRad = 2.0f * float(PI) / ScaleStepsCount; +const unsigned int ScaleLongEvery = 2; const float ScaleLongTooth = 0.1f; // in percent of radius const unsigned int SnapRegionsCount = 8; // Generates mesh for a line -GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) +static GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; @@ -40,9 +43,7 @@ GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) init_data.reserve_indices(2); // vertices - Vec3f start = Vec3f::Zero(); init_data.add_vertex(beg_pos); - Vec3f stop = Vec3f::UnitZ(); init_data.add_vertex(end_pos); // indices @@ -50,6 +51,118 @@ GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) return init_data; } +//! -- #ysFIXME those functions bodies are ported from GizmoRotation +// Generates mesh for a circle +static void init_from_circle(GLModel& model, double radius) +{ + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineLoop, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(ScaleStepsCount); + init_data.reserve_indices(ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + init_data.add_vertex(Vec3f(::cos(angle) * radius, ::sin(angle) * radius, 0.0f)); + init_data.add_index(i); + } + + model.init_from(std::move(init_data)); + model.set_color(ColorRGBA::WHITE()); +} + +// Generates mesh for a scale +static void init_from_scale(GLModel& model, double radius) +{ + const float out_radius_long = radius * (1.0f + ScaleLongTooth); + const float out_radius_short = radius * (1.0f + 0.5f * ScaleLongTooth); + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * ScaleStepRad); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * radius; + const float in_y = sina * radius; + const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; + const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + model.init_from(std::move(init_data)); + model.set_color(ColorRGBA::WHITE()); +} + +// Generates mesh for a snap_radii +static void init_from_snap_radii(GLModel& model, double radius) +{ + const float step = 2.0f * float(PI) / float(SnapRegionsCount); + const float in_radius = radius / 3.0f; + const float out_radius = 2.0f * in_radius; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(2 * ScaleStepsCount); + init_data.reserve_indices(2 * ScaleStepsCount); + + // vertices + indices + for (unsigned int i = 0; i < ScaleStepsCount; ++i) { + const float angle = float(i * step); + const float cosa = ::cos(angle); + const float sina = ::sin(angle); + const float in_x = cosa * in_radius; + const float in_y = sina * in_radius; + const float out_x = cosa * out_radius; + const float out_y = sina * out_radius; + + // vertices + init_data.add_vertex(Vec3f(in_x, in_y, 0.0f)); + init_data.add_vertex(Vec3f(out_x, out_y, 0.0f)); + + // indices + init_data.add_line(i * 2, i * 2 + 1); + } + + model.init_from(std::move(init_data)); + model.set_color(ColorRGBA::WHITE()); +} + +// Generates mesh for a angle_arc +static void init_from_angle_arc(GLModel& model, double angle, double radius) +{ + model.reset(); + + const float step_angle = float(angle) / float(AngleResolution); + const float ex_radius = radius; + + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(1 + AngleResolution); + init_data.reserve_indices(1 + AngleResolution); + + // vertices + indices + for (unsigned int i = 0; i <= AngleResolution; ++i) { + const float angle = float(i) * step_angle; + init_data.add_vertex(Vec3f(::cos(angle) * ex_radius, ::sin(angle) * ex_radius, 0.0f)); + init_data.add_index(i); + } + + model.init_from(std::move(init_data)); +} + +//! -- + #define use_grabber_extension 1 GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) @@ -101,7 +214,8 @@ std::string GLGizmoCut3D::get_tooltip() const } if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { std::string axis = m_hover_id == X ? "X" : "Y"; - return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); +// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); + return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°"); } return tooltip; @@ -496,14 +610,9 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) if (!shader) return; - glsafe(::glLineWidth(m_hover_id == X || m_hover_id == Y ? 3.0f : 1.0f)); - ColorRGBA color = picking ? picking_decode(BASE_ID - Z) : m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; - m_sphere.set_color(color); - m_cone.set_color(color); - const Camera& camera = wxGetApp().plater()->get_camera(); const Grabber& graber = m_grabbers.front(); @@ -520,125 +629,105 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) shader->set_uniform("projection_matrix", camera.get_projection_matrix()); const Transform3d view_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_rotation_m; + const Transform3d view_grabber_connection_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, m_grabber_connection_len)); - const BoundingBoxf3 tbb = transformed_bounding_box(); - const double grabber_connection_len = std::min(0.75 * m_radius, 35.0) ; + auto render = [shader, this](GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix) { + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + model.set_color(color); + model.render(); + }; - auto render_grabber_connection = [shader, view_matrix, camera, grabber_connection_len, this](bool render, const ColorRGBA& color) + auto render_rotation_snapping = [shader, camera, this](Axis axis, const ColorRGBA& color) { - if (!render) - return; - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, grabber_connection_len)); + Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_start_dragging_m; + + if (axis == X) + view_model_matrix = view_model_matrix * Geometry::rotation_transform(0.5 * PI * Vec3d::UnitY()) * Geometry::rotation_transform(-PI * Vec3d::UnitZ()); + else + view_model_matrix = view_model_matrix * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitY()); shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_grabber_connection.set_color(color); - m_grabber_connection.render(); + m_circle.render(); + m_scale.render(); + m_snap_radii.render(); + m_reference_radius.render(); + if (m_dragging) { + m_angle_arc.set_color(color); + m_angle_arc.render(); + } }; // render Z grabber - render_grabber_connection((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y, color); - - if ((!m_dragging && m_hover_id < 0 || m_hover_id == Z) && tbb.min.z() <= 0.0) { - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_cone.render(); - } + if ((!m_dragging && m_hover_id < 0)) + render(m_grabber_connection, color, view_grabber_connection_matrix); + render(m_sphere, color, view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + if (!m_dragging && m_hover_id < 0 || m_hover_id == Z) { - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones()); + const BoundingBoxf3 tbb = transformed_bounding_box(); + if (tbb.min.z() <= 0.0) + render(m_cone, color, view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_sphere.render(); - } - - if ((!m_dragging && m_hover_id < 0 || m_hover_id == Z) && tbb.max.z() >= 0.0) { - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), cone_scale); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_cone.render(); + if (tbb.max.z() >= 0.0) + render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), cone_scale)); } // render top sphere for X/Y grabbers - offset = Vec3d(0.0, 0.0, grabber_connection_len); - size = m_dragging && (m_hover_id == X || m_hover_id == Y) ? - double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); if ((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y) { - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), size * Vec3d::Ones()); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_sphere.set_color(m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : - m_hover_id == X ? complementary(ColorRGBA::RED() ) : ColorRGBA::GRAY()); - m_sphere.render(); - } - - // render Y grabber - - size = m_dragging && m_hover_id == Y ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); - cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y) - { - if (picking) - color = picking_decode(BASE_ID - Y); - else - color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); - - render_grabber_connection(m_hover_id == Y, color); - - m_cone.set_color(color); - - offset = Vec3d(1.25 * size, 0.0, grabber_connection_len); - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_cone.render(); - - offset = Vec3d(-1.25 * size, 0.0, grabber_connection_len); - view_model_matrix = view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale); - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_cone.render(); + size = m_dragging ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : + m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY(); + render(m_sphere, color, view_matrix * Geometry::assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); } // render X grabber - size = m_dragging && m_hover_id == X ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); - cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); if ((!m_dragging && m_hover_id < 0) || m_hover_id == X) { + size = m_dragging && m_hover_id == X ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); if (picking) color = picking_decode(BASE_ID - X); else color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); - render_grabber_connection(m_hover_id == X, color); + if (m_hover_id == X) { + render(m_grabber_connection, color, view_grabber_connection_matrix); + render_rotation_snapping(X, color); + } - m_cone.set_color(color); + offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); + render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); + offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); + render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); + } - offset = Vec3d(0.0, 1.25 * size, grabber_connection_len); - Transform3d view_model_matrix = view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale); + // render Y grabber - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_cone.render(); + if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y) + { + size = m_dragging && m_hover_id == Y ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + if (picking) + color = picking_decode(BASE_ID - Y); + else + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); - offset = Vec3d(0.0, -1.25 * size, grabber_connection_len); - view_model_matrix = view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale); + if (m_hover_id == Y) { + render(m_grabber_connection, color, view_grabber_connection_matrix); + render_rotation_snapping(Y, color); + } - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - m_cone.render(); + offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); + render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); + offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); + render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); } shader->stop_using(); @@ -818,35 +907,28 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) const Vec2d orig_dir = Vec2d::UnitX(); const Vec2d new_dir = mouse_pos.normalized(); + const double two_pi = 2.0 * PI; + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); if (cross2(orig_dir, new_dir) < 0.0) - theta = 2.0 * (double)PI - theta; + theta = two_pi - theta; - const double len = mouse_pos.norm(); - - // snap to coarse snap region - if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { - const double step = 2.0 * double(PI) / double(SnapRegionsCount); - theta = step * std::round(theta / step); - } - else { - // snap to fine snap region (scale) - if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { - const double step = 2.0 * double(PI) / double(ScaleStepsCount); - theta = step * std::round(theta / step); - } - } - - if (theta == 2.0 * double(PI)) + if (theta == two_pi) theta = 0.0; if (m_hover_id == X) - theta += 0.5 * double(PI); + theta += 0.5 * PI; rotation[m_hover_id] = theta; m_rotation_m = m_rotation_m * Geometry::rotation_transform(rotation); + m_angle += (float)theta; + while (m_angle > two_pi) + m_angle -= two_pi; + if (m_angle < 0.0) + m_angle += two_pi; + update_clipper(); } @@ -862,15 +944,23 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) void GLGizmoCut3D::on_start_dragging() { + m_angle = 0.0; if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move connector"), UndoRedo::SnapshotType::GizmoAction); + + if (m_hover_id == X || m_hover_id == Y) + m_start_dragging_m = m_rotation_m; } void GLGizmoCut3D::on_stop_dragging() { if (m_hover_id == X || m_hover_id == Y) { + m_angle_arc.reset(); + m_angle = 0.0; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); m_ar_rotations = Geometry::Transformation(m_rotation_m).get_rotation(); + + m_start_dragging_m = m_rotation_m; } else if (m_hover_id == Z) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction); @@ -956,15 +1046,17 @@ bool GLGizmoCut3D::update_bb() set_center_pos(m_bb_center + m_center_offset, true); m_radius = box.radius(); - m_snap_coarse_in_radius = m_radius / 3.0f; - m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; - m_snap_fine_in_radius = m_radius; - m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; + m_grabber_connection_len = std::min(0.75 * m_radius, 35.0); + m_grabber_radius = m_grabber_connection_len * 0.85; m_plane.reset(); m_cone.reset(); m_sphere.reset(); m_grabber_connection.reset(); + m_circle.reset(); + m_scale.reset(); + m_snap_radii.reset(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { m_selected.clear(); m_selected.resize(selection->model_object()->cut_connectors.size(), false); @@ -977,10 +1069,8 @@ bool GLGizmoCut3D::update_bb() void GLGizmoCut3D::on_render() { - bool updated_bb = update_bb(); - if (updated_bb) { + if (update_bb()) update_clipper_on_render(); - } if (!m_cone.is_initialized()) m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); @@ -989,6 +1079,19 @@ void GLGizmoCut3D::on_render() if (!m_grabber_connection.is_initialized()) m_grabber_connection.init_from(its_make_line(Vec3f::Zero(), Vec3f::UnitZ())); + if (!m_circle.is_initialized()) + init_from_circle(m_circle, m_grabber_radius); + if (!m_scale.is_initialized()) + init_from_scale(m_scale, m_grabber_radius); + if (!m_snap_radii.is_initialized()) + init_from_snap_radii(m_snap_radii, m_grabber_radius); + if (!m_reference_radius.is_initialized()) { + m_reference_radius.init_from(its_make_line(Vec3f::Zero(), m_grabber_connection_len * Vec3f::UnitX())); + m_reference_radius.set_color(ColorRGBA::WHITE()); + } + if (!m_angle_arc.is_initialized() || m_angle != 0.0) + init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); + if (force_update_clipper_on_render) update_clipper_on_render(); @@ -1076,35 +1179,9 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_imgui->button("Reset cutting plane")) { set_center(bounding_box().center()); m_rotation_m = Transform3d::Identity(); + m_angle_arc.reset(); update_clipper(); } - ////// - - // ImGui::AlignTextToFramePadding(); - // m_imgui->text(_L("Move center")); - - // m_imgui->disabled_begin(m_plane_center == bounding_box().center()); - // revert_move = render_revert_button("move"); - // m_imgui->disabled_end(); - - // for (Axis axis : {X, Y, Z}) - // render_move_center_input(axis); - // m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); - - // ImGui::Separator(); - - // double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - // wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); - - // Vec3d tbb_sz = transformed_bounding_box().size(); - // wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + - // ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + - // ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; - - // ImGui::AlignTextToFramePadding(); - // m_imgui->text(_L("Build size")); - // ImGui::SameLine(m_label_width); - // m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); } if (m_mode == size_t(CutMode::cutPlanar)) { @@ -1124,16 +1201,17 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(3 * m_label_width); m_imgui->disabled_begin(!m_keep_upper); - m_imgui->disabled_begin(is_approx(Geometry::Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Geometry::Transformation(m_rotation_m).get_rotation().y(), 0.)); + m_imgui->disabled_begin(is_approx(Geometry::Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Geometry::Transformation(m_rotation_m).get_rotation().y(), 0.)); if (m_imgui->checkbox(_L("Place on cut") + "##upper", m_place_on_cut_upper)) m_rotate_upper = false; + m_imgui->disabled_end(); + ImGui::SameLine(); if (m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper)) m_place_on_cut_upper = false; m_imgui->disabled_end(); - m_imgui->disabled_end(); m_imgui->text(""); ImGui::SameLine(m_label_width); @@ -1158,8 +1236,10 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); if (m_imgui->button(_L("Add/Edit connectors"))) m_connectors_editing = true; + m_imgui->disabled_end(); } else { // connectors mode m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); // Connectors section @@ -1235,6 +1315,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) set_center(bounding_box().center()); if (revert_rotation) { m_rotation_m = Transform3d::Identity(); + m_angle_arc.reset(); update_clipper(); } } @@ -1592,6 +1673,7 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), cross_dir).toRotationMatrix(); m_rotation_m = m; + m_angle_arc.reset(); set_center(m_plane_center + cross_dir * (cross_dir.dot(pt - m_plane_center))); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index dbb1e6ae0..b562396c1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -36,11 +36,13 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_center_offset{ Vec3d::Zero() }; // values from RotationGizmo - float m_radius{ 0.0f }; - float m_snap_coarse_in_radius{ 0.0f }; - float m_snap_coarse_out_radius{ 0.0f }; - float m_snap_fine_in_radius{ 0.0f }; - float m_snap_fine_out_radius{ 0.0f }; + double m_radius{ 0.0 }; + double m_grabber_radius{ 0.0 }; + double m_grabber_connection_len{ 0.0 }; + + // dragging angel in hovered axes + Transform3d m_start_dragging_m{ Transform3d::Identity() }; + double m_angle{ 0.0 }; GLModel m_connector_shape; TriangleMesh m_connector_mesh; @@ -56,6 +58,13 @@ class GLGizmoCut3D : public GLGizmoBase GLModel m_cut_line; GLModel m_cone; GLModel m_sphere; + + GLModel m_circle; + GLModel m_scale; + GLModel m_snap_radii; + GLModel m_reference_radius; + GLModel m_angle_arc; + Vec3d m_old_center; #endif // ENABLE_LEGACY_OPENGL_REMOVAL From 0fd29dfec7b6d5366a94b31652e3103e80a12d30 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 21 Jul 2022 17:04:37 +0200 Subject: [PATCH 51/93] Cut WIP: Suppress un-universal scaling for cut objects Added editing of the tolerance --- src/libslic3r/Model.cpp | 122 ++++++++++++++-------- src/libslic3r/Model.hpp | 27 +++-- src/slic3r/GUI/GUI_ObjectList.cpp | 7 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 17 ++- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 48 ++++++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 5 +- 7 files changed, 155 insertions(+), 72 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 36034d904..b18756a5e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -713,7 +713,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, ModelVolumeType t ModelVolume* v = new ModelVolume(this, other); if (type != ModelVolumeType::INVALID && v->type() != type) v->set_type(type); - v->source.is_connector = other.source.is_connector; + v->cut_info = other.cut_info; this->volumes.push_back(v); // The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull. // v->center_geometry_after_creation(); @@ -1380,10 +1380,9 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) { - // discard old connector markers vor volumes - for (ModelVolume* volume : volumes) { - volume->source.is_connector = false; - } + // discard old connector markers for volumes + for (ModelVolume* volume : volumes) + volume->cut_info.discard(); if (cut_connectors.empty()) return; @@ -1404,8 +1403,8 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr Vec3d::Ones() )); + new_volume->cut_info = { true, connector.radius_tolerance, connector.height_tolerance }; new_volume->name = name + "-" + std::to_string(++connector_id); - new_volume->source.is_connector = true; } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1481,27 +1480,47 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const dowels->input_file.clear(); } + using namespace Geometry; + // Because transformations are going to be applied to meshes directly, // we reset transformation of all instances and volumes, // except for translation and Z-rotation on instances, which are preserved // in the transformation matrix and not applied to the mesh transform. // const auto instance_matrix = instances[instance]->get_matrix(true); - const auto instance_matrix = Geometry::assemble_transform( + const auto instance_matrix = assemble_transform( Vec3d::Zero(), // don't apply offset instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), instances[instance]->get_scaling_factor(), instances[instance]->get_mirror() ); - const auto cut_matrix = Geometry::assemble_transform(-cut_center); - - const auto invert_cut_matrix = Geometry::assemble_transform(cut_center, cut_rotation); + const auto cut_matrix = rotation_transform(cut_rotation).inverse() * assemble_transform(-cut_center); + const auto invert_cut_matrix = assemble_transform(cut_center, cut_rotation); // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); Vec3d local_dowels_displace = Vec3d::Zero(); + Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); + + auto apply_tolerance = [](ModelVolume * vol) + { + Vec3d sf = vol->get_scaling_factor(); +/* + // correct Z offset in respect to the new size + Vec3d pos = vol->get_offset(); + pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance; + vol->set_offset(pos); +*/ + // make a "hole" wider + sf[X] *= (1 + vol->cut_info.radius_tolerance); + sf[Y] *= (1 + vol->cut_info.radius_tolerance); + // make a "hole" dipper + sf[Z] *= (1 + vol->cut_info.height_tolerance); + vol->set_scaling_factor(sf); + }; + for (ModelVolume* volume : volumes) { const auto volume_matrix = volume->get_matrix(); @@ -1510,43 +1529,33 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const volume->mmu_segmentation_facets.reset(); if (!volume->is_model_part()) { - if (volume->source.is_connector) { + if (volume->cut_info.is_connector) { + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - - Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper) ? - Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix : - instance_matrix * volume_matrix; - volume->set_transformation(m); - ModelVolume* vol = upper->add_volume(*volume); - // make a "hole" dipper - vol->set_scaling_factor(Z, 1.1 * vol->get_scaling_factor(Z)); + vol->set_transformation(volume_matrix); + apply_tolerance(vol); } if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - - Transform3d m = attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? - Geometry::rotation_transform(Geometry::deg2rad(180.0) * Vec3d::UnitX()) * Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix : - instance_matrix * volume_matrix; - volume->set_transformation(m); ModelVolume* vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); + if (attributes.has(ModelObjectCutAttribute::CreateDowels)) - // make a "hole" dipper - vol->set_scaling_factor(Z, 1.2 * vol->get_scaling_factor(Z)); + apply_tolerance(vol); else // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { // add one more solid part same as connector if this connector is a dowel - // But discard rotation and Z-offset for this volume - volume->set_rotation(Vec3d::Zero()); - Vec3d offset = volume->get_offset(); - offset[Z] = 0.0; - volume->set_offset(offset); - ModelVolume* vol = dowels->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + // Compute the displacement (in instance coordinates) to be applied to place the dowels local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); } @@ -1556,19 +1565,18 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // to the modifier volume transformation to preserve their shape properly. volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); + // #ysFIXME - add logic for the negative volumes/connectors if (attributes.has(ModelObjectCutAttribute::KeepUpper)) upper->add_volume(*volume); if (attributes.has(ModelObjectCutAttribute::KeepLower)) lower->add_volume(*volume); } } - else if (!volume->mesh().empty() -// && !volume->source.is_connector // we don't allow to cut a connectors - ) { + else if (!volume->mesh().empty()) { // Transform the mesh by the combined transformation matrix. // Flip the triangles in case the composite transformation is left handed. TriangleMesh mesh(volume->mesh()); - mesh.transform(Geometry::rotation_transform(cut_rotation).inverse() * cut_matrix * instance_matrix * volume_matrix, true); + mesh.transform(cut_matrix * instance_matrix* volume_matrix, true); volume->reset_mesh(); // Reset volume transformation except for offset @@ -1588,8 +1596,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const } if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { - if (!attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) - upper_mesh.transform(invert_cut_matrix); + upper_mesh.transform(invert_cut_matrix); ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; @@ -1600,10 +1607,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const vol->set_material(volume->material_id(), *volume->material()); } if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) - lower_mesh.transform(Geometry::assemble_transform(Vec3d::Zero(), Geometry::deg2rad(180.0)*Vec3d::UnitX())); - else - lower_mesh.transform(invert_cut_matrix); + lower_mesh.transform(invert_cut_matrix); ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; @@ -1643,7 +1647,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const obj_instance->set_transformation(Geometry::Transformation()); obj_instance->set_offset(offset + displace); - obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipUpper) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z)); + + Vec3d rotation = Vec3d::Zero(); + if (attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) { + Transform3d trafo = rotation_transform(cut_rotation).inverse(); + if (i != instance) + trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; + rotation = Transformation(trafo).get_rotation(); + } + else if (attributes.has(ModelObjectCutAttribute::FlipUpper)) { + rotation = rotate_z180; + if (i != instance) + rotation[Z] = rot_z; + } + else if (i != instance) + rotation[Z] = rot_z/* * Vec3d::UnitZ()*/; + obj_instance->set_rotation(rotation); } res.push_back(upper); @@ -1666,7 +1685,22 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const const double rot_z = obj_instance->get_rotation().z(); obj_instance->set_transformation(Geometry::Transformation()); obj_instance->set_offset(offset); - obj_instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, i == instance ? 0.0 : rot_z)); + + Vec3d rotation = Vec3d::Zero(); + if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) { + Transform3d trafo = rotation_transform(rotate_z180) * rotation_transform(cut_rotation).inverse(); + if (i != instance) + trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; + rotation = Transformation(trafo).get_rotation(); + } + else if (attributes.has(ModelObjectCutAttribute::FlipLower)) { + rotation = rotate_z180; + if (i != instance) + rotation[Z] = rot_z; + } + else if (i != instance) + rotation[Z] = rot_z; + obj_instance->set_rotation(rotation); } res.push_back(lower); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 6afa2efe8..3242dd255 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -225,18 +225,20 @@ struct CutConnector Vec3d rotation; float radius; float height; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] bool failed = false; CutConnector() - : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f) + : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} - CutConnector(Vec3d p, Vec3d rot, float r, float h, bool fl = false) - : pos(p), rotation(rot), radius(r), height(h), failed(fl) + CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, bool fl = false) + : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), failed(fl) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.failed) {} + CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.failed) {} bool operator==(const CutConnector& sp) const; @@ -251,7 +253,7 @@ struct CutConnector */ template inline void serialize(Archive& ar) { - ar(pos, rotation, radius, height, failed); + ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, failed); } static constexpr size_t steps = 32; @@ -696,16 +698,25 @@ public: bool is_converted_from_meters{ false }; bool is_from_builtin_objects{ false }; - bool is_connector{ false }; - template void serialize(Archive& ar) { //FIXME Vojtech: Serialize / deserialize only if the Source is set. // likely testing input_file or object_idx would be sufficient. - ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects, is_connector); + ar(input_file, object_idx, volume_idx, mesh_offset, transform, is_converted_from_inches, is_converted_from_meters, is_from_builtin_objects); } }; Source source; + // struct used by cut command + // It contains information about connetors + struct CutInfo + { + bool is_connector {false}; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] + + void discard() { is_connector = false; } + } cut_info; + // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 3b5af8cd9..23ee84a5c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2499,6 +2499,7 @@ void ObjectList::part_selection_changed() bool enable_manipulation {true}; bool disable_ss_manipulation {false}; + bool disable_ununiform_scale {false}; const auto item = GetSelection(); @@ -2543,6 +2544,7 @@ void ObjectList::part_selection_changed() disable_ss_manipulation = true; break; } + disable_ununiform_scale = !cut_objects.empty(); } } } @@ -2649,8 +2651,11 @@ void ObjectList::part_selection_changed() if (disable_ss_manipulation) wxGetApp().obj_manipul()->DisableScale(); - else + else { wxGetApp().obj_manipul()->Enable(enable_manipulation); + if (disable_ununiform_scale) + wxGetApp().obj_manipul()->DisableUnuniformScale(); + } } if (update_and_show_settings) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index b3f71687b..c89ee949a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -580,7 +580,11 @@ void ObjectManipulation::Enable(const bool enadle) { for (auto editor : m_editors) editor->Enable(enadle); - for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt }) + for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_reset_rotation_button, m_drop_to_bed_button, m_check_inch, m_lock_bnt +#if ENABLE_WORLD_COORDINATE + ,m_reset_skew_button +#endif // ENABLE_WORLD_COORDINATE + }) win->Enable(enadle); } @@ -588,10 +592,19 @@ void ObjectManipulation::DisableScale() { for (auto editor : m_editors) editor->Enable(editor->has_opt_key("scale") || editor->has_opt_key("size") ? false : true); - for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_lock_bnt }) + for (wxWindow* win : std::initializer_list{ m_reset_scale_button, m_lock_bnt +#if ENABLE_WORLD_COORDINATE + ,m_reset_skew_button +#endif // ENABLE_WORLD_COORDINATE + }) win->Enable(false); } +void ObjectManipulation::DisableUnuniformScale() +{ + m_lock_bnt->disable(); +} + void ObjectManipulation::update_ui_from_settings() { if (m_imperial_units != (wxGetApp().app_config->get("use_inches") == "1")) { diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 936ac99d8..2a4b3e46a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -201,6 +201,7 @@ public: void Enable(const bool enadle = true); void Disable() { Enable(false); } void DisableScale(); + void DisableUnuniformScale(); void update_ui_from_settings(); bool use_colors() { return m_use_colors; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 5fd0b28d4..417cf6037 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -430,12 +430,12 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i return old_val != value; } -bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double& value_in) +bool GLGizmoCut3D::render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in) { ImGui::AlignTextToFramePadding(); m_imgui->text(label); ImGui::SameLine(m_label_width); - ImGui::PushItemWidth(m_control_width); + ImGui::PushItemWidth(m_control_width * 0.85f); float value = (float)value_in; if (m_imperial_units) @@ -443,15 +443,25 @@ bool GLGizmoCut3D::render_slicer_double_input(const std::string& label, double& float old_val = value; const BoundingBoxf3 bbox = bounding_box(); - const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); - - m_imgui->slider_float(("##" + label).c_str(), &value, 1.0f, mean_size); - - ImGui::SameLine(); - m_imgui->text(m_imperial_units ? _L("in") : _L("mm")); + float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); + float min_size = 1.f; + if (m_imperial_units) { + mean_size *= ObjectManipulation::mm_to_in; + min_size *= ObjectManipulation::mm_to_in; + } + std::string format = m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); + m_imgui->slider_float(("##" + label).c_str(), &value, min_size, mean_size, format.c_str()); value_in = (double)(value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0)); - return old_val != value; + + ImGui::SameLine(m_label_width + m_control_width + 3); + ImGui::PushItemWidth(m_control_width * 0.3f); + + float old_tolerance, tolerance = old_tolerance = (float)tolerance_in; + m_imgui->slider_float(("##tolerance_" + label).c_str(), &tolerance, 1.f, 20.f, "%.f %%", 1.f, true, _L("Tolerance")); + tolerance_in = (int)tolerance; + + return old_val != value || old_tolerance != tolerance; } void GLGizmoCut3D::render_move_center_input(int axis) @@ -1253,11 +1263,11 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_imgui->button(" " + _L("Reset") + " ##connectors")) reset_connectors(); m_imgui->disabled_end(); - +/* m_imgui->text(_L("Mode")); render_connect_mode_radio_button(CutConnectorMode::Auto); render_connect_mode_radio_button(CutConnectorMode::Manual); - +*/ m_imgui->text(_L("Type")); render_connect_type_radio_button(CutConnectorType::Plug); render_connect_type_radio_button(CutConnectorType::Dowel); @@ -1267,12 +1277,16 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) update_connector_shape(); - if (render_slicer_double_input(_u8L("Depth ratio"), m_connector_depth_ratio)) - for (auto& connector : connectors) + if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + for (auto& connector : connectors) { connector.height = float(m_connector_depth_ratio); - if (render_slicer_double_input(_u8L("Size"), m_connector_size)) - for (auto& connector : connectors) + connector.height_tolerance = 0.01f * m_connector_depth_ratio; + } + if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) + for (auto& connector : connectors) { connector.radius = float(m_connector_size * 0.5); + connector.radius_tolerance = 0.01f * m_connector_size_tolerance; + } m_imgui->disabled_end(); @@ -1710,7 +1724,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi //std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio)); + connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), + float(m_connector_size * 0.5), float(m_connector_depth_ratio), + float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance)); update_model_object(); m_selected.push_back(false); assert(m_selected.size() == connectors.size()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index b562396c1..91f8749e7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -81,6 +81,9 @@ class GLGizmoCut3D : public GLGizmoBase double m_connector_depth_ratio{ 3.0 }; double m_connector_size{ 2.5 }; + int m_connector_depth_ratio_tolerance{ 10 }; + int m_connector_size_tolerance{ 0 }; + float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; bool m_imperial_units{ false }; @@ -174,7 +177,7 @@ private: void set_center(const Vec3d& center); bool render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); bool render_double_input(const std::string& label, double& value_in); - bool render_slicer_double_input(const std::string& label, double& value_in); + bool render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in); void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); From e990254d52db00ad84d033586d7231a22d899a13 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 26 Jul 2022 16:31:01 +0200 Subject: [PATCH 52/93] Cut WIP: set attributes for each connector separately. + Allow select/deselect several connectors and apply size/depth for selected group of connectors --- src/libslic3r/Model.cpp | 12 +- src/libslic3r/Model.hpp | 105 ++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 191 +++++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 8 +- 4 files changed, 172 insertions(+), 144 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index b18756a5e..79273bbbe 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1378,7 +1378,7 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn return connector_mesh; } -void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes) +void ModelObject::apply_cut_connectors(const std::string& name) { // discard old connector markers for volumes for (ModelVolume* volume : volumes) @@ -1387,11 +1387,9 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr if (cut_connectors.empty()) return; - indexed_triangle_set connector_mesh = get_connector_mesh(connector_attributes); - size_t connector_id = cut_id.connectors_cnt(); for (const CutConnector& connector : cut_connectors) { - TriangleMesh mesh = TriangleMesh(connector_mesh); + TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); // Mesh will be centered when loading. ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); @@ -1403,7 +1401,7 @@ void ModelObject::apply_cut_connectors(const std::string& name, CutConnectorAttr Vec3d::Ones() )); - new_volume->cut_info = { true, connector.radius_tolerance, connector.height_tolerance }; + new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; new_volume->name = name + "-" + std::to_string(++connector_id); } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1541,13 +1539,13 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const ModelVolume* vol = lower->add_volume(*volume); vol->set_transformation(volume_matrix); - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) + if (volume->cut_info.connector_type == CutConnectorType::Dowel) apply_tolerance(vol); else // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { + if (volume->cut_info.connector_type == CutConnectorType::Dowel) { // add one more solid part same as connector if this connector is a dowel ModelVolume* vol = dowels->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3242dd255..a82c08136 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -219,48 +219,6 @@ private: friend class ModelObject; }; -struct CutConnector -{ - Vec3d pos; - Vec3d rotation; - float radius; - float height; - float radius_tolerance;// [0.f : 1.f] - float height_tolerance;// [0.f : 1.f] - bool failed = false; - - CutConnector() - : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) - {} - - CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, bool fl = false) - : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), failed(fl) - {} - - CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.failed) {} - - bool operator==(const CutConnector& sp) const; - - bool operator!=(const CutConnector& sp) const { return !(sp == (*this)); } -/* - bool is_inside(const Vec3f& pt) const; - - bool get_intersections(const Vec3f& s, const Vec3f& dir, - std::array, 2>& out) const; - - indexed_triangle_set to_mesh() const; -*/ - template inline void serialize(Archive& ar) - { - ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, failed); - } - - static constexpr size_t steps = 32; -}; - -using CutConnectors = std::vector; - enum class CutConnectorType : int { Plug , Dowel @@ -283,8 +241,8 @@ enum class CutConnectorShape : int { struct CutConnectorAttributes { CutConnectorType type{ CutConnectorType::Plug }; - CutConnectorStyle style{ CutConnectorStyle::Prizm}; - CutConnectorShape shape{CutConnectorShape::Circle}; + CutConnectorStyle style{ CutConnectorStyle::Prizm }; + CutConnectorShape shape{ CutConnectorShape::Circle }; CutConnectorAttributes() {} @@ -294,8 +252,55 @@ struct CutConnectorAttributes CutConnectorAttributes(const CutConnectorAttributes& rhs) : CutConnectorAttributes(rhs.type, rhs.style, rhs.shape) {} + + bool operator==(const CutConnectorAttributes& other) const; + + bool operator!=(const CutConnectorAttributes& other) const { return !(other == (*this)); } + + bool operator<(const CutConnectorAttributes& other) const { + return this->type < other.type || + (this->type == other.type && this->style < other.style) || + (this->type == other.type && this->style == other.style && this->shape < other.shape); + } + + template inline void serialize(Archive& ar) { + ar(type, style, shape); + } }; +struct CutConnector +{ + Vec3d pos; + Vec3d rotation; + float radius; + float height; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] + CutConnectorAttributes attribs; + + CutConnector() + : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) + {} + + CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) + : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + {} + + CutConnector(const CutConnector& rhs) : + CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + + bool operator==(const CutConnector& other) const; + + bool operator!=(const CutConnector& other) const { return !(other == (*this)); } + + template inline void serialize(Archive& ar) { + ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, attribs); + } +}; + +using CutConnectors = std::vector; + + // Declared outside of ModelVolume, so it could be forward declared. enum class ModelVolumeType : int { INVALID = -1, @@ -436,7 +441,7 @@ public: size_t parts_count() const; ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); - void apply_cut_connectors(const std::string& name, CutConnectorAttributes connector_attributes); + void apply_cut_connectors(const std::string& name); // invalidate cut state for this and related objects from the whole model void invalidate_cut(); void synchronize_model_after_cut(); @@ -710,12 +715,14 @@ public: // It contains information about connetors struct CutInfo { - bool is_connector {false}; - float radius_tolerance;// [0.f : 1.f] - float height_tolerance;// [0.f : 1.f] + bool is_connector{ false }; + CutConnectorType connector_type{ CutConnectorType::Plug }; + float radius_tolerance;// [0.f : 1.f] + float height_tolerance;// [0.f : 1.f] void discard() { is_connector = false; } - } cut_info; + }; + CutInfo cut_info; // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 417cf6037..21917b1d6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -226,12 +226,15 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.Moving() && !cut_line_processing()) return false; - if (use_grabbers(mouse_event)) - return true; - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); + if (use_grabbers(mouse_event)) { + if (m_hover_id >= m_connectors_group_id && mouse_event.LeftUp() && !mouse_event.ShiftDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + return true; + } + static bool pending_right_up = false; if (mouse_event.LeftDown()) { bool grabber_contains_mouse = (get_hover_id() != -1); @@ -407,6 +410,9 @@ bool GLGizmoCut3D::render_combo(const std::string& label, const std::vectorradio_button(m_connector_types[size_t(type)], m_connector_type == type)) { m_connector_type = type; update_connector_shape(); + return true; } + return false; } void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode) @@ -1268,31 +1276,63 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_connect_mode_radio_button(CutConnectorMode::Auto); render_connect_mode_radio_button(CutConnectorMode::Manual); */ + + if (m_selected_count == 1) + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + auto& connector = connectors[idx]; + m_connector_depth_ratio = connector.height; + m_connector_depth_ratio_tolerance = 100 * connector.height_tolerance; + m_connector_size = 2. * connector.radius; + m_connector_size_tolerance = 100 * connector.radius_tolerance; + m_connector_type = connector.attribs.type; + m_connector_style = size_t(connector.attribs.style); + m_connector_shape_id = size_t(connector.attribs.shape); + + break; + } + m_imgui->text(_L("Type")); - render_connect_type_radio_button(CutConnectorType::Plug); - render_connect_type_radio_button(CutConnectorType::Dowel); + bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); + type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); + if (type_changed) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.type = CutConnectorType(m_connector_type); if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) - update_connector_shape(); - if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) - update_connector_shape(); + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.style = CutConnectorStyle(m_connector_style) ; + + if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); + + if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) { + auto& connector = connectors[idx]; + connector.height = float(m_connector_depth_ratio); + connector.height_tolerance = 0.01f * m_connector_depth_ratio_tolerance; + } - if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) - for (auto& connector : connectors) { - connector.height = float(m_connector_depth_ratio); - connector.height_tolerance = 0.01f * m_connector_depth_ratio; - } if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) - for (auto& connector : connectors) { - connector.radius = float(m_connector_size * 0.5); - connector.radius_tolerance = 0.01f * m_connector_size_tolerance; - } + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + auto& connector = connectors[idx]; + connector.radius = float(m_connector_size * 0.5); + connector.radius_tolerance = 0.01f * m_connector_size_tolerance; + } m_imgui->disabled_end(); if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_connectors_editing = false; + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; } m_parent.request_extra_frame(); } @@ -1380,7 +1420,6 @@ void GLGizmoCut3D::render_connectors(bool picking) m_selected.resize(connectors.size(), false); } -#if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -1388,19 +1427,8 @@ void GLGizmoCut3D::render_connectors(bool picking) shader->start_using(); ScopeGuard guard([shader]() { shader->stop_using(); }); -#else - GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_shader("gouraud_light"); - if (shader) - shader->start_using(); - ScopeGuard guard([shader]() { if (shader) shader->stop_using(); }); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL -#if ENABLE_GL_SHADERS_ATTRIBUTES const Camera& camera = wxGetApp().plater()->get_camera(); -#else - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES ColorRGBA render_color; @@ -1417,13 +1445,12 @@ void GLGizmoCut3D::render_connectors(bool picking) for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; -// const bool& point_selected = m_selected[i]; double height = connector.height; // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; - if (m_connector_type == CutConnectorType::Dowel && - m_connector_style == size_t(CutConnectorStyle::Prizm)) { + if (connector.attribs.type == CutConnectorType::Dowel && + connector.attribs.style == CutConnectorStyle::Prizm) { pos -= height * normal; height *= 2; } @@ -1435,7 +1462,13 @@ void GLGizmoCut3D::render_connectors(bool picking) else { if (size_t(m_hover_id- m_connectors_group_id) == i) render_color = ColorRGBA::CYAN(); - else { // neither hover nor picking + else if (m_selected[i]) + render_color = ColorRGBA::DARK_GRAY(); + else // neither hover nor picking + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + + // ! #ysFIXME rework get_volume_transformation + if (0) { // else { // neither hover nor picking int mesh_id = -1; for (const ModelVolume* mv : mo->volumes) { ++mesh_id; @@ -1454,42 +1487,18 @@ void GLGizmoCut3D::render_connectors(bool picking) } } -#if ENABLE_GL_SHADERS_ATTRIBUTES - m_connector_shape.set_color(render_color); + m_shapes[connector.attribs].set_color(render_color); const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( - Vec3d(pos.x(), pos.y(), pos.z()), + pos, Geometry::Transformation(m_rotation_m).get_rotation(), - Vec3d(connector.radius, connector.radius, height), - Vec3d::Ones() + Vec3d(connector.radius, connector.radius, height) ); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#else - const_cast(&m_connector_shape)->set_color(-1, render_color); - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(pos.x(), pos.y(), pos.z())); - - const Vec3d& angles = m_rotation_gizmo.get_rotation(); - glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); - - glsafe(::glTranslated(0., 0., -0.5*connector.height)); - glsafe(::glScaled(connector.radius, connector.radius, height)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES - - m_connector_shape.render(); - -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif //!ENABLE_GL_SHADERS_ATTRIBUTES + m_shapes[connector.attribs].render(); } - -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif //!ENABLE_GL_SHADERS_ATTRIBUTES } bool GLGizmoCut3D::can_perform_cut() const @@ -1535,9 +1544,10 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) for (CutConnector& connector : mo->cut_connectors) { connector.rotation = rotation; - if (m_connector_type == CutConnectorType::Dowel) { - if (m_connector_style == size_t(CutConnectorStyle::Prizm)) + if (connector.attribs.type == CutConnectorType::Dowel) { + if (connector.attribs.style == CutConnectorStyle::Prizm) connector.height *= 2; + create_dowels_as_separate_object = true; } else { // culculate shift of the connector center regarding to the position on the cut plane @@ -1551,9 +1561,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) connector.pos += norm * (0.5 * connector.height); } } - mo->apply_cut_connectors(_u8L("Connector"), CutConnectorAttributes(CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); - if (m_connector_type == CutConnectorType::Dowel) - create_dowels_as_separate_object = true; + mo->apply_cut_connectors(_u8L("Connector")); } } @@ -1623,15 +1631,15 @@ void GLGizmoCut3D::reset_connectors() void GLGizmoCut3D::update_connector_shape() { - if (m_connector_shape.is_initialized()) - m_connector_shape.reset(); - - const indexed_triangle_set its = ModelObject::get_connector_mesh({ m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }); - m_connector_shape.init_from(its); + CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; + if (m_shapes.find(attribs) == m_shapes.end()) { + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + m_shapes[attribs].init_from(its); + } + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); m_connector_mesh.clear(); m_connector_mesh = TriangleMesh(its); - } void GLGizmoCut3D::update_model_object() const @@ -1709,14 +1717,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - // left down without selection rectangle - place connector on the cut plane: - if (action == SLAGizmoEventType::LeftDown && /*!m_selection_rectangle.is_dragging() && */!shift_down && m_connectors_editing) { - // If any point is in hover state, this should initiate its move - return control back to GLCanvas: - if (m_hover_id != -1) - return false; - - // If there is some selection, don't add new point and deselect everything instead. - if (m_selection_empty) { + if (action == SLAGizmoEventType::LeftDown && !shift_down && m_connectors_editing) { + // If there is no selection and no hovering, add new point + if (m_hover_id == -1 && !control_down && !alt_down) { std::pair pos_and_normal; if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { const Vec3d& hit = pos_and_normal.first; @@ -1726,12 +1729,15 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio), - float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance)); + float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); update_model_object(); - m_selected.push_back(false); + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected.push_back(true); assert(m_selected.size() == connectors.size()); m_parent.set_as_dirty(); - m_wait_for_up_event = true; return true; } @@ -1739,6 +1745,23 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi } return true; } + else if (action == SLAGizmoEventType::LeftUp && !shift_down && m_connectors_editing) { + if (m_hover_id >= m_connectors_group_id) { + if (alt_down) { + m_selected[m_hover_id - m_connectors_group_id] = false; + --m_selected_count; + } + else { + if (!control_down) { + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; + } + m_selected[m_hover_id - m_connectors_group_id] = true; + ++m_selected_count; + } + return true; + } + } else if (action == SLAGizmoEventType::RightDown && !shift_down && m_connectors_editing) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id < m_connectors_group_id) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 91f8749e7..fecb3e846 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -12,6 +12,7 @@ namespace Slic3r { enum class CutConnectorType : int; class ModelVolume; +struct CutConnectorAttributes; namespace GUI { class Selection; @@ -44,7 +45,7 @@ class GLGizmoCut3D : public GLGizmoBase Transform3d m_start_dragging_m{ Transform3d::Identity() }; double m_angle{ 0.0 }; - GLModel m_connector_shape; + std::map m_shapes; TriangleMesh m_connector_mesh; // workaround for using of the clipping plane normal Vec3d m_clp_normal{ Vec3d::Ones() }; @@ -90,8 +91,7 @@ class GLGizmoCut3D : public GLGizmoBase bool force_update_clipper_on_render{false}; mutable std::vector m_selected; // which pins are currently selected - bool m_selection_empty = true; - bool m_wait_for_up_event = false; + int m_selected_count{ 0 }; bool m_has_invalid_connector{ false }; @@ -181,7 +181,7 @@ private: void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_revert_button(const std::string& label); - void render_connect_type_radio_button(CutConnectorType type); + bool render_connect_type_radio_button(CutConnectorType type); Transform3d get_volume_transformation(const ModelVolume* volume) const; void render_connectors(bool picking); From 27f7a8da0f12ec344c646ae82575d2b472099a58 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 27 Jul 2022 13:53:54 +0200 Subject: [PATCH 53/93] Cut WIP: Added shortcuts for "Edit connectors" ImGuiDialog + Added processing for the Ctrl+A ("Select All connectors") --- resources/icons/collapse_btn.svg | 13 +++++++++++ resources/icons/expand_btn.svg | 12 ++++++++++ src/imgui/imconfig.h | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 28 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 +++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 2 ++ 7 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 resources/icons/collapse_btn.svg create mode 100644 resources/icons/expand_btn.svg diff --git a/resources/icons/collapse_btn.svg b/resources/icons/collapse_btn.svg new file mode 100644 index 000000000..4ee221a44 --- /dev/null +++ b/resources/icons/collapse_btn.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/icons/expand_btn.svg b/resources/icons/expand_btn.svg new file mode 100644 index 000000000..32d7f9959 --- /dev/null +++ b/resources/icons/expand_btn.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 9a29789d3..f9fdf575b 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -170,6 +170,8 @@ namespace ImGui const wchar_t LegendShells = 0x2616; const wchar_t LegendToolMarker = 0x2617; const wchar_t WarningMarkerSmall = 0x2618; + const wchar_t ExpandBtn = 0x2619; + const wchar_t CollapseBtn = 0x2620; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 21917b1d6..71a8bd5ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -783,6 +783,17 @@ bool GLGizmoCut3D::on_init() m_grabbers.emplace_back(); m_shortcut_key = WXK_CONTROL_C; + // initiate info shortcuts + const wxString ctrl = GUI::shortkey_ctrl_prefix(); + const wxString alt = GUI::shortkey_alt_prefix(); + + m_shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add connector"))); + m_shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove connector"))); + m_shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move connector"))); + m_shortcuts.push_back(std::make_pair(ctrl + _L("Left click"), _L("Add connector to selection"))); + m_shortcuts.push_back(std::make_pair(alt + _L("Left click"), _L("Remove connector from selection"))); + m_shortcuts.push_back(std::make_pair(ctrl + "A", _L("Select all connectors"))); + return true; } @@ -1258,7 +1269,18 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_imgui->button(_L("Add/Edit connectors"))) m_connectors_editing = true; m_imgui->disabled_end(); - } else { // connectors mode + } + else { // connectors mode + if (m_imgui->button("? " + (m_show_shortcuts ? wxString(ImGui::CollapseBtn) : wxString(ImGui::ExpandBtn)))) + m_show_shortcuts = !m_show_shortcuts; + + if (m_show_shortcuts) + for (const auto& shortcut : m_shortcuts ){ + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, shortcut.first); + ImGui::SameLine(m_label_width); + m_imgui->text(shortcut.second); + } + m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); // Connectors section ImGui::Separator(); @@ -1778,6 +1800,10 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi return true; } + else if (action == SLAGizmoEventType::SelectAll) { + std::fill(m_selected.begin(), m_selected.end(), true); + return true; + } return false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index fecb3e846..170928a07 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -98,6 +98,9 @@ class GLGizmoCut3D : public GLGizmoBase Matrix3d m_rotation_matrix; Vec3d m_rotations{ Vec3d::Zero() }; + bool m_show_shortcuts{ false }; + std::vector> m_shortcuts; + enum class CutMode { cutPlanar , cutGrig diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 09276ef57..070417520 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -505,7 +505,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ { // Sla gizmo selects all support points - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::SelectAll)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == Cut) && gizmo_event(SLAGizmoEventType::SelectAll)) processed = true; break; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 8c27db56e..8b4befa77 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -76,6 +76,8 @@ static const std::map font_icons = { #endif // ENABLE_LEGEND_TOOLBAR_ICONS {ImGui::RevertButton , "undo" }, {ImGui::WarningMarkerSmall , "notification_warning" }, + {ImGui::ExpandBtn , "expand_btn" }, + {ImGui::CollapseBtn , "collapse_btn" }, }; static const std::map font_icons_large = { From 05c22604fbba81cb3ba0ec197017a1fdc66e80b9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 27 Jul 2022 15:21:37 +0200 Subject: [PATCH 54/93] Cut WIP: Suppress use connectors for SLA mode --- src/libslic3r/Model.cpp | 8 ++++++++ src/libslic3r/Model.hpp | 2 ++ src/slic3r/GUI/GUI_App.cpp | 7 +++++++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 16 ++++++++++------ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 79273bbbe..d62f56105 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2682,6 +2682,14 @@ bool model_has_multi_part_objects(const Model &model) return false; } +bool model_has_connectors(const Model &model) +{ + for (const ModelObject *model_object : model.objects) + if (!model_object->cut_connectors.empty()) + return true; + return false; +} + bool model_has_advanced_features(const Model &model) { auto config_is_advanced = [](const ModelConfig &config) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a82c08136..d056368bd 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1338,6 +1338,8 @@ extern bool model_mmu_segmentation_data_changed(const ModelObject& mo, const Mod // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. bool model_has_multi_part_objects(const Model &model); +// If the model has objects with cut connectrs, then it is currently not supported by the SLA mode. +bool model_has_connectors(const Model& model); // If the model has advanced features, then it cannot be processed in simple mode. bool model_has_advanced_features(const Model &model); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 42e325de7..e4e5fffcf 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2869,6 +2869,13 @@ bool GUI_App::may_switch_to_SLA_preset(const wxString& caption) caption); return false; } + if (model_has_connectors(model())) { + show_info(nullptr, + _L("SLA technology doesn't support cut with connectors") + "\n\n" + + _L("Please check your object list before preset changing."), + caption); + return false; + } return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 71a8bd5ba..490f67a60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1176,6 +1176,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) bool cut_clicked = false; bool revert_move{ false }; bool revert_rotation{ false }; + bool fff_printer = wxGetApp().plater()->printer_technology() == ptFFF; if (! m_connectors_editing) { if (m_mode == size_t(CutMode::cutPlanar)) { @@ -1263,12 +1264,14 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->disabled_end(); } - ImGui::Separator(); + if (fff_printer) { + ImGui::Separator(); - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) - m_connectors_editing = true; - m_imgui->disabled_end(); + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); + if (m_imgui->button(_L("Add/Edit connectors"))) + m_connectors_editing = true; + m_imgui->disabled_end(); + } } else { // connectors mode if (m_imgui->button("? " + (m_show_shortcuts ? wxString(ImGui::CollapseBtn) : wxString(ImGui::ExpandBtn)))) @@ -1361,7 +1364,8 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); - m_imgui->text(m_has_invalid_connector ? wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.") : wxString()); + if (fff_printer) + m_imgui->text(m_has_invalid_connector ? wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.") : wxString()); if (!m_connectors_editing) { m_imgui->disabled_begin(!can_perform_cut()); cut_clicked = m_imgui->button(_L("Perform cut")); From 31800bb85dccd3dc3161c20b297d5238ad934581 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 28 Jul 2022 14:23:51 +0200 Subject: [PATCH 55/93] GizmoScale: Suppress ununiversal scale for cut objects + Gizmos/GLGizmoRotate: Deleted changes which was made for GizmoCut, but aren't used any more --- src/slic3r/GUI/GUI_ObjectList.cpp | 7 ++++++- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 17 +++++------------ src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 5 ----- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 6 ++++++ src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 5 +++++ src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 8 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 23ee84a5c..87992e958 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -12,6 +12,7 @@ #include "MainFrame.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "Gizmos/GLGizmoCut.hpp" +#include "Gizmos/GLGizmoScale.hpp" #include "OptionsGroup.hpp" #include "Tab.hpp" @@ -2503,6 +2504,8 @@ void ObjectList::part_selection_changed() const auto item = GetSelection(); + GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); + if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { og_name = _L("Group manipulation"); @@ -2579,7 +2582,6 @@ void ObjectList::part_selection_changed() info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam : info_type == InfoItemType::Cut ? GLGizmosManager::EType::Cut : GLGizmosManager::EType::MmuSegmentation; - GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); if (gizmos_mgr.get_current_type() != gizmo_type) gizmos_mgr.open_gizmo(gizmo_type); if (info_type == InfoItemType::Cut) { @@ -2656,6 +2658,9 @@ void ObjectList::part_selection_changed() if (disable_ununiform_scale) wxGetApp().obj_manipul()->DisableUnuniformScale(); } + + if (GLGizmoScale3D* scale = dynamic_cast(gizmos_mgr.get_gizmo(GLGizmosManager::Scale))) + scale->enable_ununiversal_scale(!disable_ununiform_scale); } if (update_and_show_settings) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index c89ee949a..a0c0cb00a 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -602,7 +602,7 @@ void ObjectManipulation::DisableScale() void ObjectManipulation::DisableUnuniformScale() { - m_lock_bnt->disable(); + m_lock_bnt->Enable(false); } void ObjectManipulation::update_ui_from_settings() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index d82fbd587..72bc955d0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -58,12 +58,6 @@ void GLGizmoRotate::set_angle(double angle) m_angle = angle; } -void GLGizmoRotate::set_center(const Vec3d& center) -{ - m_forced_center = center; - m_has_forced_center = true; -} - std::string GLGizmoRotate::get_tooltip() const { std::string axis; @@ -301,13 +295,13 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); if (coordinates_type == ECoordinatesType::World) { m_bounding_box = selection.get_bounding_box(); - m_center = m_has_forced_center ? m_forced_center : m_bounding_box.center(); + m_center = m_bounding_box.center(); } else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) { const GLVolume& v = *selection.get_first_volume(); m_bounding_box = v.transformed_convex_hull_bounding_box( v.get_instance_transformation().get_scaling_factor_matrix() * v.get_volume_transformation().get_scaling_factor_matrix()); - m_center = v.world_matrix() * (m_has_forced_center ? m_forced_center : m_bounding_box.center()); + m_center = v.world_matrix() * m_bounding_box.center(); } else { m_bounding_box.reset(); @@ -318,7 +312,7 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) } const Geometry::Transformation inst_trafo = selection.get_first_volume()->get_instance_transformation(); m_bounding_box = m_bounding_box.transformed(inst_trafo.get_scaling_factor_matrix()); - m_center = inst_trafo.get_matrix_no_scaling_factor() * (m_has_forced_center ? m_forced_center : m_bounding_box.center()); + m_center = inst_trafo.get_matrix_no_scaling_factor() * m_bounding_box.center(); } m_radius = Offset + m_bounding_box.radius(); @@ -867,7 +861,7 @@ GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_fil bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) { - if (mouse_event.Dragging() && m_dragging && !m_use_only_grabbers) { + if (mouse_event.Dragging() && m_dragging) { // Apply new temporary rotations #if ENABLE_WORLD_COORDINATE TransformationType transformation_type; @@ -954,8 +948,7 @@ void GLGizmoRotate3D::on_start_dragging() void GLGizmoRotate3D::on_stop_dragging() { assert(0 <= m_hover_id && m_hover_id < 3); - if (!m_use_only_grabbers) - m_parent.do_rotate(L("Gizmo-Rotate")); + m_parent.do_rotate(L("Gizmo-Rotate")); m_gizmos[m_hover_id].stop_dragging(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 9db024b14..5f1de8151 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -34,7 +34,6 @@ private: float m_snap_coarse_out_radius{ 0.0f }; float m_snap_fine_in_radius{ 0.0f }; float m_snap_fine_out_radius{ 0.0f }; - bool m_has_forced_center{false}; Vec3d m_forced_center{ Vec3d::Zero() }; #if ENABLE_WORLD_COORDINATE BoundingBoxf3 m_bounding_box; @@ -70,7 +69,6 @@ public: double get_angle() const { return m_angle; } void set_angle(double angle); - void set_center(const Vec3d& center); std::string get_tooltip() const override; @@ -135,15 +133,12 @@ private: class GLGizmoRotate3D : public GLGizmoBase { std::array m_gizmos; - bool m_use_only_grabbers{ false }; public: GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation.x()); m_gizmos[Y].set_angle(rotation.y()); m_gizmos[Z].set_angle(rotation.z()); } - void set_center(const Vec3d& center) { m_gizmos[X].set_center(center); m_gizmos[Y].set_center(center); m_gizmos[Z].set_center(center); } - void use_only_grabbers() { m_use_only_grabbers = true; } std::string get_tooltip() const override { std::string tooltip = m_gizmos[X].get_tooltip(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 4312a6122..dae862f8f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -104,6 +104,12 @@ bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event) return use_grabbers(mouse_event); } +void GLGizmoScale3D::enable_ununiversal_scale(bool enable) +{ + for (unsigned int i = 0; i < 6; ++i) + m_grabbers[i].enabled = enable; +} + void GLGizmoScale3D::data_changed() { #if ENABLE_WORLD_COORDINATE diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index fb4ff09b6..a18b176ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -85,6 +85,7 @@ public: bool on_mouse(const wxMouseEvent &mouse_event) override; void data_changed() override; + void enable_ununiversal_scale(bool enable); protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 070417520..9e0138262 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -1042,6 +1042,11 @@ GLGizmoBase* GLGizmosManager::get_current() const return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get(); } +GLGizmoBase* GLGizmosManager::get_gizmo(GLGizmosManager::EType type) const +{ + return ((type == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[type].get(); +} + GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& gizmo_name) const { std::vector selectable_idxs = get_selectable_idxs(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 2e9e6bb65..6e2a19bc0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -199,6 +199,7 @@ public: EType get_current_type() const { return m_current; } GLGizmoBase* get_current() const; + GLGizmoBase* get_gizmo(GLGizmosManager::EType type) const; EType get_gizmo_from_name(const std::string& gizmo_name) const; bool is_running() const; From a7930cdedd8c72511d5e0f22c7e8178a87a782b4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 28 Jul 2022 17:01:05 +0200 Subject: [PATCH 56/93] Cut WIP: Cut by line: Some rework for its behavior. Line can be drawn by : Shift + 1. LeftDown, Dragging, LeftUp 2. LeftDown, LeftUp, Move, LeftDown --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 75 +++++++++++----------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 490f67a60..da06551b8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -223,12 +223,23 @@ std::string GLGizmoCut3D::get_tooltip() const bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) { - if (mouse_event.Moving() && !cut_line_processing()) - return false; - Vec2i mouse_coord(mouse_event.GetX(), mouse_event.GetY()); Vec2d mouse_pos = mouse_coord.cast(); + if (mouse_event.ShiftDown() && mouse_event.LeftDown()) + return gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + if (cut_line_processing()) { + if (mouse_event.ShiftDown()) { + if (mouse_event.Moving()|| mouse_event.Dragging()) + return gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + if (mouse_event.LeftUp()) + return gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + } + discard_cut_line_processing(); + } + else if (mouse_event.Moving()) + return false; + if (use_grabbers(mouse_event)) { if (m_hover_id >= m_connectors_group_id && mouse_event.LeftUp() && !mouse_event.ShiftDown()) gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); @@ -281,11 +292,6 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return true; } } - else if (mouse_event.Moving()) { - // draw cut line - gizmo_event(SLAGizmoEventType::Moving, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); - return true; - } else if (pending_right_up && mouse_event.RightUp()) { pending_right_up = false; return true; @@ -543,7 +549,6 @@ void GLGizmoCut3D::render_cut_plane() if (cut_line_processing()) return; -#if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; @@ -554,7 +559,7 @@ void GLGizmoCut3D::render_cut_plane() glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); shader->start_using(); -#if ENABLE_GL_SHADERS_ATTRIBUTES + const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( m_plane_center, @@ -564,14 +569,6 @@ void GLGizmoCut3D::render_cut_plane() ); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#else - const Vec3d& angles = m_rotation_gizmo.get_rotation(); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(m_plane_center.x(), m_plane_center.y(), m_plane_center.z())); - glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES if (!m_plane.is_initialized()) { GLModel::Geometry init_data; @@ -580,17 +577,12 @@ void GLGizmoCut3D::render_cut_plane() init_data.reserve_vertices(4); init_data.reserve_indices(6); - const BoundingBoxf3 bb = bounding_box(); - const float min_x = bb.min.x() - Margin - m_plane_center.x(); - const float max_x = bb.max.x() + Margin - m_plane_center.x(); - const float min_y = bb.min.y() - Margin - m_plane_center.y(); - const float max_y = bb.max.y() + Margin - m_plane_center.y(); - // vertices - init_data.add_vertex(Vec3f(min_x, min_y, 0.0)); - init_data.add_vertex(Vec3f(max_x, min_y, 0.0)); - init_data.add_vertex(Vec3f(max_x, max_y, 0.0)); - init_data.add_vertex(Vec3f(min_x, max_y, 0.0)); + float radius = (float)bounding_box().radius(); + init_data.add_vertex(Vec3f(-radius, -radius, 0.0)); + init_data.add_vertex(Vec3f( radius, -radius, 0.0)); + init_data.add_vertex(Vec3f( radius, radius, 0.0)); + init_data.add_vertex(Vec3f(-radius, radius, 0.0)); // indices init_data.add_triangle(0, 1, 2); @@ -600,19 +592,6 @@ void GLGizmoCut3D::render_cut_plane() } m_plane.render(); -#if !ENABLE_GL_SHADERS_ATTRIBUTES - glsafe(::glPopMatrix()); -#endif //!ENABLE_GL_SHADERS_ATTRIBUTES -#else - // Draw the cutting plane - ::glBegin(GL_QUADS); - ::glColor4fv(PLANE_COLOR.data()); - ::glVertex3f(min_x, min_y, plane_center.z()); - ::glVertex3f(max_x, min_y, plane_center.z()); - ::glVertex3f(max_x, max_y, plane_center.z()); - ::glVertex3f(min_x, max_y, plane_center.z()); - glsafe(::glEnd()); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -1685,6 +1664,11 @@ bool GLGizmoCut3D::cut_line_processing() const return m_line_beg != Vec3d::Zero(); } +void GLGizmoCut3D::discard_cut_line_processing() +{ + m_line_beg = m_line_end = Vec3d::Zero(); +} + bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position) { const float sla_shift = m_c->selection_info()->get_sla_shift(); @@ -1710,9 +1694,10 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse if (cut_line_processing()) { m_line_end = pt; - if (action == SLAGizmoEventType::LeftDown) { - Vec3d point = m_line_end; + if (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp) { Vec3d line_dir = m_line_end - m_line_beg; + if (line_dir.norm() < 3.0) + return true; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by line"), UndoRedo::SnapshotType::GizmoAction); Vec3d cross_dir = line_dir.cross(dir).normalized(); @@ -1725,7 +1710,7 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse set_center(m_plane_center + cross_dir * (cross_dir.dot(pt - m_plane_center))); - m_line_end = m_line_beg = Vec3d::Zero(); + discard_cut_line_processing(); } return true; } @@ -1738,7 +1723,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi return false; if ( m_hover_id < 0 && shift_down && ! m_connectors_editing && - (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::Moving) ) + (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::Moving) ) return process_cut_line(action, mouse_position); CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 170928a07..863461d50 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -190,6 +190,7 @@ private: bool can_perform_cut() const; bool cut_line_processing() const; + void discard_cut_line_processing(); void render_cut_plane(); void render_cut_center_graber(bool picking = false); From dda346b70af9a58e68aef626316ddb60398ead51 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 8 Aug 2022 14:58:13 +0200 Subject: [PATCH 57/93] After merge fixes --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 4 +++- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 34 +++++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 - src/slic3r/GUI/MeshUtils.cpp | 8 +++---- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 5c1a7c346..5122ac320 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -403,7 +403,9 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { } if (mouse_event.LeftDown()) { - Selection &selection = m_parent.get_selection(); + Selection &selection = m_parent.get_selection(); + if (!selection.is_empty() && m_hover_id != -1 && + (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { selection.setup_cache(); m_dragging = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 757f4e571..31a3276c1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -214,8 +214,8 @@ std::string GLGizmoCut3D::get_tooltip() const } if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { std::string axis = m_hover_id == X ? "X" : "Y"; -// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); - return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°"); +// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); + return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°"); } return tooltip; @@ -608,12 +608,10 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) return; #if ENABLE_GL_CORE_PROFILE - if (!OpenGLManager::get_gl_info().is_core_profile()) + if (!OpenGLManager::get_gl_info().is_core_profile()) #endif // ENABLE_GL_CORE_PROFILE - glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); -#if ENABLE_LEGACY_OPENGL_REMOVAL - if (!m_grabber_connection.is_initialized() || is_changed) { - m_grabber_connection.reset(); + glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); + ColorRGBA color = picking ? picking_decode(BASE_ID - Z) : m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; @@ -753,7 +751,7 @@ void GLGizmoCut3D::render_cut_line() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES + m_cut_line.reset(); m_cut_line.init_from(its_make_line((Vec3f)m_line_beg.cast(), (Vec3f)m_line_end.cast())); @@ -822,26 +820,32 @@ void GLGizmoCut3D::on_set_state() } else m_c->object_clipper()->release(); -#endif // !ENABLE_GL_CORE_PROFILE + force_update_clipper_on_render = m_state == On; } #if ENABLE_RAYCAST_PICKING -void GLGizmoCut::on_register_raycasters_for_picking() +void GLGizmoCut3D::on_register_raycasters_for_picking() { // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account m_parent.set_raycaster_gizmos_on_top(true); } -void GLGizmoCut::on_unregister_raycasters_for_picking() +void GLGizmoCut3D::on_unregister_raycasters_for_picking() { m_parent.set_raycaster_gizmos_on_top(false); } #else +void GLGizmoCut3D::on_render_for_picking() +{ + render_cut_center_graber(true); + render_connectors(true); +} +#endif // ENABLE_RAYCAST_PICKING + void GLGizmoCut3D::on_set_hover_id() { } -#endif // ENABLE_RAYCAST_PICKING bool GLGizmoCut3D::on_is_activable() const { @@ -1139,12 +1143,6 @@ void GLGizmoCut3D::on_render() render_cut_line(); } -void GLGizmoCut3D::on_render_for_picking() -{ - render_cut_center_graber(true); - render_connectors(true); -} - void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) { static float last_y = 0.0f; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 5fc43462a..3987081ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -106,7 +106,6 @@ class GLGizmoCut3D : public GLGizmoBase , cutGrig //,cutRadial //,cutModular - std::vector volumes_trafos; }; enum class CutConnectorMode { diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index bd38354aa..bbf1f7652 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -127,11 +127,9 @@ void MeshClipper::render_contour() GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader != nullptr) { shader->start_using(); -#if ENABLE_GL_SHADERS_ATTRIBUTES const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); -#endif // ENABLE_GL_SHADERS_ATTRIBUTES m_model_expanded.set_color(color); m_model_expanded.render(); shader->stop_using(); @@ -418,7 +416,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& Vec3d direction; line_from_mouse_pos(mouse_pos, trafo, camera, point, direction); - std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector hits = m_emesh.query_ray_hits(point, direction); if (hits.empty()) return false; // no intersection found @@ -434,8 +432,8 @@ bool MeshRaycaster::is_valid_intersection(Vec3d point, Vec3d direction, const Tr { point = trafo.inverse() * point; - std::vector hits = m_emesh.query_ray_hits(point, direction); - std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); + std::vector hits = m_emesh.query_ray_hits(point, direction); + std::vector neg_hits = m_emesh.query_ray_hits(point, -direction); return !hits.empty() && !neg_hits.empty(); } From df8f7e1069c57f944cb0a57197b08d5b2b2fcee7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 10 Aug 2022 11:25:04 +0200 Subject: [PATCH 58/93] Cut WIP: Raycasters for picking are applied + Added snapping for rotation of the cut plane --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 353 +++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 30 ++- 3 files changed, 280 insertions(+), 111 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 5122ac320..c3a443d0b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -404,14 +404,14 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) { if (mouse_event.LeftDown()) { Selection &selection = m_parent.get_selection(); - if (!selection.is_empty() && m_hover_id != -1 && - (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))) { + if (!selection.is_empty() && m_hover_id != -1 /* && + (m_grabbers.empty() || m_hover_id < static_cast(m_grabbers.size()))*/) { selection.setup_cache(); m_dragging = true; for (auto &grabber : m_grabbers) grabber.dragging = false; - if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) - m_grabbers[m_hover_id].dragging = true; +// if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size())) +// m_grabbers[m_hover_id].dragging = true; on_start_dragging(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 31a3276c1..8d38bb295 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -241,8 +241,14 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) return false; if (use_grabbers(mouse_event)) { - if (m_hover_id >= m_connectors_group_id && mouse_event.LeftUp() && !mouse_event.ShiftDown()) - gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + if (m_hover_id >= m_connectors_group_id) { + if (mouse_event.LeftDown()) { + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; + } + if (mouse_event.LeftUp() && !mouse_event.ShiftDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); + } return true; } @@ -366,6 +372,11 @@ void GLGizmoCut3D::update_clipper() m_c->object_clipper()->set_range_and_pos(normal, offset, dist); put_connetors_on_cut_plane(normal, offset); + + if (m_raycasters.empty()) + on_register_raycasters_for_picking(); + else + update_raycasters_for_picking_transform(); } void GLGizmoCut3D::update_clipper_on_render() @@ -599,21 +610,15 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } -void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) +void GLGizmoCut3D::render_cut_center_graber() { glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (!shader) return; -#if ENABLE_GL_CORE_PROFILE - if (!OpenGLManager::get_gl_info().is_core_profile()) -#endif // ENABLE_GL_CORE_PROFILE - glsafe(::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f)); - - ColorRGBA color = picking ? picking_decode(BASE_ID - Z) : - m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; + ColorRGBA color = m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; const Camera& camera = wxGetApp().plater()->get_camera(); const Grabber& graber = m_grabbers.front(); @@ -631,7 +636,6 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) shader->set_uniform("projection_matrix", camera.get_projection_matrix()); const Transform3d view_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_rotation_m; - const Transform3d view_grabber_connection_matrix = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, m_grabber_connection_len)); auto render = [shader, this](GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix) { shader->set_uniform("view_model_matrix", view_model_matrix); @@ -640,6 +644,29 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) model.render(); }; + auto render_grabber_connection = [shader, camera, view_matrix, this](const ColorRGBA& color) + { + shader->stop_using(); + GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); + if (!line_shader) + return; + + line_shader->start_using(); + line_shader->set_uniform("emission_factor", 0.1f); + line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + const Transform3d trafo = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, m_grabber_connection_len)); + line_shader->set_uniform("view_model_matrix", trafo); + line_shader->set_uniform("normal_matrix", (Matrix3d)trafo.matrix().block(0, 0, 3, 3).inverse().transpose()); + line_shader->set_uniform("width", 0.2f); + + m_grabber_connection.set_color(color); + m_grabber_connection.render(); + + line_shader->stop_using(); + shader->start_using(); + }; + auto render_rotation_snapping = [shader, camera, this](Axis axis, const ColorRGBA& color) { Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_start_dragging_m; @@ -649,33 +676,48 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) else view_model_matrix = view_model_matrix * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitY()); - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + shader->stop_using(); + + GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); + if (!line_shader) + return; + + line_shader->start_using(); + line_shader->set_uniform("emission_factor", 0.1f); + line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + line_shader->set_uniform("view_model_matrix", view_model_matrix); + line_shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + line_shader->set_uniform("width", 0.25f); m_circle.render(); m_scale.render(); m_snap_radii.render(); m_reference_radius.render(); if (m_dragging) { + line_shader->set_uniform("width", 1.5f); m_angle_arc.set_color(color); m_angle_arc.render(); } + + line_shader->stop_using(); + shader->start_using(); }; // render Z grabber if ((!m_dragging && m_hover_id < 0)) - render(m_grabber_connection, color, view_grabber_connection_matrix); - render(m_sphere, color, view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + render_grabber_connection(color); + render(m_sphere.model, color, view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); if (!m_dragging && m_hover_id < 0 || m_hover_id == Z) { const BoundingBoxf3 tbb = transformed_bounding_box(); if (tbb.min.z() <= 0.0) - render(m_cone, color, view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); + render(m_cone.model, color, view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); if (tbb.max.z() >= 0.0) - render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), cone_scale)); + render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), cone_scale)); } // render top sphere for X/Y grabbers @@ -685,7 +727,7 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) size = m_dragging ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY(); - render(m_sphere, color, view_matrix * Geometry::assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); + render(m_sphere.model, color, view_matrix * Geometry::assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); } // render X grabber @@ -694,20 +736,17 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) { size = m_dragging && m_hover_id == X ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - if (picking) - color = picking_decode(BASE_ID - X); - else - color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); + color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); if (m_hover_id == X) { - render(m_grabber_connection, color, view_grabber_connection_matrix); + render_grabber_connection(color); render_rotation_snapping(X, color); } offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); - render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); + render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); - render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); + render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); } // render Y grabber @@ -716,20 +755,17 @@ void GLGizmoCut3D::render_cut_center_graber(bool picking /* = false*/) { size = m_dragging && m_hover_id == Y ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); - if (picking) - color = picking_decode(BASE_ID - Y); - else - color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); + color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); if (m_hover_id == Y) { - render(m_grabber_connection, color, view_grabber_connection_matrix); + render_grabber_connection(color); render_rotation_snapping(Y, color); } offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); - render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); + render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); - render(m_cone, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); + render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); } shader->stop_using(); @@ -742,15 +778,15 @@ void GLGizmoCut3D::render_cut_line() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glLineWidth(2.0f)); - GLShaderProgram* shader = wxGetApp().get_shader("flat"); + GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("width", 0.25f); m_cut_line.reset(); m_cut_line.init_from(its_make_line((Vec3f)m_line_beg.cast(), (Vec3f)m_line_end.cast())); @@ -824,24 +860,122 @@ void GLGizmoCut3D::on_set_state() force_update_clipper_on_render = m_state == On; } -#if ENABLE_RAYCAST_PICKING void GLGizmoCut3D::on_register_raycasters_for_picking() { - // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account - m_parent.set_raycaster_gizmos_on_top(true); + assert(m_raycasters.empty()); + set_volumes_picking_state(false); + + init_picking_models(); + + if (m_connectors_editing) { + if (CommonGizmosDataObjects::SelectionInfo* si = m_c->selection_info()) { + const CutConnectors& connectors = si->model_object()->cut_connectors; + for (size_t i = 0; i < connectors.size(); ++i) + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i + m_connectors_group_id, *(m_shapes[connectors[i].attribs]).mesh_raycaster, Transform3d::Identity())); + } + } + else { + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, X, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, X, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Y, *m_cone.mesh_raycaster, Transform3d::Identity())); + + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_sphere.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, Z, *m_cone.mesh_raycaster, Transform3d::Identity())); + } + + update_raycasters_for_picking_transform(); } void GLGizmoCut3D::on_unregister_raycasters_for_picking() { - m_parent.set_raycaster_gizmos_on_top(false); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_raycasters.clear(); + set_volumes_picking_state(true); } -#else -void GLGizmoCut3D::on_render_for_picking() + +void GLGizmoCut3D::set_volumes_picking_state(bool state) { - render_cut_center_graber(true); - render_connectors(true); + std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (raycasters != nullptr) { + const Selection& selection = m_parent.get_selection(); + const Selection::IndicesList ids = selection.get_volume_idxs(); + for (unsigned int id : ids) { + const GLVolume* v = selection.get_volume(id); + auto it = std::find_if(raycasters->begin(), raycasters->end(), [v](std::shared_ptr item) { return item->get_raycaster() == v->mesh_raycaster.get(); }); + if (it != raycasters->end()) + (*it)->set_active(state); + } + } +} + +void GLGizmoCut3D::update_raycasters_for_picking_transform() +{ + if (m_connectors_editing) { + CommonGizmosDataObjects::SelectionInfo* si = m_c->selection_info(); + if (!si) + return; + const ModelObject* mo = si->model_object(); + const CutConnectors& connectors = mo->cut_connectors; + if (connectors.empty()) + return; + auto inst_id = m_c->selection_info()->get_active_instance(); + if (inst_id < 0) + return; + + const Vec3d& instance_offset = mo->instances[inst_id]->get_offset(); + const float sla_shift = m_c->selection_info()->get_sla_shift(); + + const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); + const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; + + for (size_t i = 0; i < connectors.size(); ++i) { + const CutConnector& connector = connectors[i]; + + double height = connector.height; + // recalculate connector position to world position + Vec3d pos = connector.pos + instance_offset; + if (connector.attribs.type == CutConnectorType::Dowel && + connector.attribs.style == CutConnectorStyle::Prizm) { + pos -= height * normal; + height *= 2; + } + pos[Z] += sla_shift; + + m_raycasters[i]->set_transform(Geometry::assemble_transform( + pos, + Geometry::Transformation(m_rotation_m).get_rotation(), + Vec3d(connector.radius, connector.radius, height) + )); + } + } + else { + const Transform3d trafo = Geometry::translation_transform(m_plane_center) * m_rotation_m; + + const BoundingBoxf3 box = bounding_box(); + const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 6.0); + + double size = double(m_grabbers.front().get_half_size(mean_size)); + Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); + + Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); + m_raycasters[0]->set_transform(trafo * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), scale)); + offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); + m_raycasters[1]->set_transform(trafo * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), scale)); + + offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); + m_raycasters[2]->set_transform(trafo * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), scale)); + offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); + m_raycasters[3]->set_transform(trafo * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), scale)); + + offset = 1.25 * size * Vec3d::UnitZ(); + m_raycasters[4]->set_transform(trafo * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + m_raycasters[5]->set_transform(trafo * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), scale)); + m_raycasters[6]->set_transform(trafo * Geometry::assemble_transform(offset, Vec3d::Zero(), scale)); + } } -#endif // ENABLE_RAYCAST_PICKING void GLGizmoCut3D::on_set_hover_id() { @@ -856,7 +990,7 @@ bool GLGizmoCut3D::on_is_activable() const Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const { - double half_pi = 0.5 * double(PI); + double half_pi = 0.5 * PI; Transform3d m = Transform3d::Identity(); @@ -882,8 +1016,7 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse } } - m = m * m_rotation_m.inverse(); - + m = m * m_start_dragging_m.inverse(); m.translate(-m_plane_center); return transform(mouse_ray, m).intersect_plane(0.0); @@ -945,17 +1078,29 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) if (cross2(orig_dir, new_dir) < 0.0) theta = two_pi - theta; + const double len = mouse_pos.norm(); + // snap to coarse snap region + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = two_pi / double(SnapRegionsCount); + theta = step * std::round(theta / step); + } + else { + // snap to fine snap region (scale) + if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = two_pi / double(ScaleStepsCount); + theta = step * std::round(theta / step); + } + } + if (theta == two_pi) theta = 0.0; - if (m_hover_id == X) theta += 0.5 * PI; rotation[m_hover_id] = theta; + m_rotation_m = m_start_dragging_m * Geometry::rotation_transform(rotation); - m_rotation_m = m_rotation_m * Geometry::rotation_transform(rotation); - - m_angle += (float)theta; + m_angle = theta; while (m_angle > two_pi) m_angle -= two_pi; if (m_angle < 0.0) @@ -964,13 +1109,13 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) update_clipper(); } - else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) { std::pair pos_and_normal; if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) return; connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; + update_raycasters_for_picking_transform(); } } @@ -1081,6 +1226,11 @@ bool GLGizmoCut3D::update_bb() m_grabber_connection_len = std::min(0.75 * m_radius, 35.0); m_grabber_radius = m_grabber_connection_len * 0.85; + m_snap_coarse_in_radius = m_grabber_radius / 3.0f; + m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_fine_in_radius = m_grabber_connection_len * 0.85; + m_snap_fine_out_radius = m_grabber_connection_len * 1.15f; + m_plane.reset(); m_cone.reset(); m_sphere.reset(); @@ -1099,18 +1249,29 @@ bool GLGizmoCut3D::update_bb() return false; } +void GLGizmoCut3D::init_picking_models() +{ + if (!m_cone.model.is_initialized()) { + indexed_triangle_set its = its_make_cone(1.0, 1.0, PI / 12.0); + m_cone.model.init_from(its); + m_cone.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } + if (!m_sphere.model.is_initialized()) { + indexed_triangle_set its = its_make_sphere(1.0, PI / 12.0); + m_sphere.model.init_from(its); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } +} + void GLGizmoCut3D::on_render() { if (update_bb()) update_clipper_on_render(); - if (!m_cone.is_initialized()) - m_cone.init_from(its_make_cone(1.0, 1.0, double(PI) / 12.0)); - if (!m_sphere.is_initialized()) - m_sphere.init_from(its_make_sphere(1.0, double(PI) / 12.0)); + init_picking_models(); + if (!m_grabber_connection.is_initialized()) m_grabber_connection.init_from(its_make_line(Vec3f::Zero(), Vec3f::UnitZ())); - if (!m_circle.is_initialized()) init_from_circle(m_circle, m_grabber_radius); if (!m_scale.is_initialized()) @@ -1124,10 +1285,12 @@ void GLGizmoCut3D::on_render() if (!m_angle_arc.is_initialized() || m_angle != 0.0) init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); - if (force_update_clipper_on_render) + if (force_update_clipper_on_render) { update_clipper_on_render(); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + } - render_connectors(false); + render_connectors(); if (! m_connectors_editing) ::glDisable(GL_DEPTH_TEST); @@ -1265,8 +1428,10 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) ImGui::Separator(); m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) + if (m_imgui->button(_L("Add/Edit connectors"))) { m_connectors_editing = true; + on_unregister_raycasters_for_picking(); + } m_imgui->disabled_end(); } } @@ -1353,6 +1518,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_connectors_editing = false; + on_unregister_raycasters_for_picking(); std::fill(m_selected.begin(), m_selected.end(), false); m_selected_count = 0; } @@ -1422,9 +1588,9 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c return Geometry::assemble_transform(offset, Vec3d::Zero(), Vec3d::Ones() - border_scale, Vec3d::Ones()) * vol_matrix; } -void GLGizmoCut3D::render_connectors(bool picking) +void GLGizmoCut3D::render_connectors() { - if (picking && ! m_connectors_editing) + if (!m_connectors_editing) return; ::glEnable(GL_DEPTH_TEST); @@ -1443,7 +1609,7 @@ void GLGizmoCut3D::render_connectors(bool picking) m_selected.resize(connectors.size(), false); } - GLShaderProgram* shader = picking ? wxGetApp().get_shader("flat") : wxGetApp().get_shader("gouraud_light"); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -1480,37 +1646,33 @@ void GLGizmoCut3D::render_connectors(bool picking) pos[Z] += sla_shift; // First decide about the color of the point. - if (picking) - render_color = picking_decode(BASE_ID - i - m_connectors_group_id); - else { - if (size_t(m_hover_id- m_connectors_group_id) == i) - render_color = ColorRGBA::CYAN(); - else if (m_selected[i]) - render_color = ColorRGBA::DARK_GRAY(); - else // neither hover nor picking - render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + if (size_t(m_hover_id- m_connectors_group_id) == i) + render_color = ColorRGBA::CYAN(); + else if (m_selected[i]) + render_color = ColorRGBA::DARK_GRAY(); + else // neither hover nor picking + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); - // ! #ysFIXME rework get_volume_transformation - if (0) { // else { // neither hover nor picking - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - ++mesh_id; - if (!mv->is_model_part()) - continue; + // ! #ysFIXME rework get_volume_transformation + if (0) { // else { // neither hover nor picking + int mesh_id = -1; + for (const ModelVolume* mv : mo->volumes) { + ++mesh_id; + if (!mv->is_model_part()) + continue; - const Transform3d volume_trafo = get_volume_transformation(mv); + const Transform3d volume_trafo = get_volume_transformation(mv); - if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { - render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : /*ColorRGBA(0.5f, 0.5f, 0.5f, 1.f)*/ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); - break; - } - render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); - m_has_invalid_connector = true; + if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : /*ColorRGBA(0.5f, 0.5f, 0.5f, 1.f)*/ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + break; } + render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + m_has_invalid_connector = true; } } - m_shapes[connector.attribs].set_color(render_color); + m_shapes[connector.attribs].model.set_color(render_color); const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( pos, @@ -1520,7 +1682,7 @@ void GLGizmoCut3D::render_connectors(bool picking) shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - m_shapes[connector.attribs].render(); + m_shapes[connector.attribs].model.render(); } } @@ -1657,7 +1819,8 @@ void GLGizmoCut3D::update_connector_shape() CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; if (m_shapes.find(attribs) == m_shapes.end()) { const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); - m_shapes[attribs].init_from(its); + m_shapes[attribs].model.init_from(its); + m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its)));; } const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); @@ -1665,16 +1828,16 @@ void GLGizmoCut3D::update_connector_shape() m_connector_mesh = TriangleMesh(its); } -void GLGizmoCut3D::update_model_object() const +void GLGizmoCut3D::update_model_object() { -/* const Selection& selection = m_parent.get_selection(); - const GLVolume* first_glvolume = selection.get_first_volume(); - const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box();*/ const ModelObjectPtrs& mos = wxGetApp().model().objects; ModelObject* mo = m_c->selection_info()->model_object(); wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); } bool GLGizmoCut3D::cut_line_processing() const @@ -1763,10 +1926,10 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi CutConnectorAttributes( CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); - update_model_object(); std::fill(m_selected.begin(), m_selected.end(), false); m_selected.push_back(true); assert(m_selected.size() == connectors.size()); + update_model_object(); m_parent.set_as_dirty(); return true; @@ -1801,9 +1964,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi size_t connector_id = m_hover_id - m_connectors_group_id; connectors.erase(connectors.begin() + connector_id); - update_model_object(); m_selected.erase(m_selected.begin() + connector_id); assert(m_selected.size() == connectors.size()); + update_model_object(); m_parent.set_as_dirty(); return true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 3987081ef..1665b8549 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -41,11 +41,15 @@ class GLGizmoCut3D : public GLGizmoBase double m_grabber_radius{ 0.0 }; double m_grabber_connection_len{ 0.0 }; + double m_snap_coarse_in_radius{ 0.0 }; + double m_snap_coarse_out_radius{ 0.0 }; + double m_snap_fine_in_radius{ 0.0 }; + double m_snap_fine_out_radius{ 0.0 }; + // dragging angel in hovered axes Transform3d m_start_dragging_m{ Transform3d::Identity() }; double m_angle{ 0.0 }; - std::map m_shapes; TriangleMesh m_connector_mesh; // workaround for using of the clipping plane normal Vec3d m_clp_normal{ Vec3d::Ones() }; @@ -53,12 +57,14 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_line_beg{ Vec3d::Zero() }; Vec3d m_line_end{ Vec3d::Zero() }; -#if ENABLE_LEGACY_OPENGL_REMOVAL GLModel m_plane; GLModel m_grabber_connection; GLModel m_cut_line; - GLModel m_cone; - GLModel m_sphere; + + PickingModel m_sphere; + PickingModel m_cone; + std::map m_shapes; + std::vector> m_raycasters; GLModel m_circle; GLModel m_scale; @@ -67,7 +73,6 @@ class GLGizmoCut3D : public GLGizmoBase GLModel m_angle_arc; Vec3d m_old_center; -#endif // ENABLE_LEGACY_OPENGL_REMOVAL bool m_keep_upper{ true }; bool m_keep_lower{ true }; @@ -168,12 +173,12 @@ protected: void on_start_dragging() override; void on_stop_dragging() override; void on_render() override; -#if ENABLE_RAYCAST_PICKING + virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; -#else - virtual void on_render_for_picking() override; -#endif // ENABLE_RAYCAST_PICKING + void set_volumes_picking_state(bool state); + void update_raycasters_for_picking_transform(); + void on_render_input_window(float x, float y, float bottom_limit) override; bool wants_enter_leave_snapshots() const override { return true; } @@ -191,21 +196,22 @@ private: bool render_revert_button(const std::string& label); bool render_connect_type_radio_button(CutConnectorType type); Transform3d get_volume_transformation(const ModelVolume* volume) const; - void render_connectors(bool picking); + void render_connectors(); bool can_perform_cut() const; bool cut_line_processing() const; void discard_cut_line_processing(); void render_cut_plane(); - void render_cut_center_graber(bool picking = false); + void render_cut_center_graber(); void render_cut_line(); void perform_cut(const Selection& selection); void set_center_pos(const Vec3d& center_pos, bool force = false); bool update_bb(); + void init_picking_models(); void reset_connectors(); void update_connector_shape(); - void update_model_object() const; + void update_model_object(); bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); }; From 7912613dc8e11db2ddea8018ce16b26a01756a3b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 12 Aug 2022 17:59:36 +0200 Subject: [PATCH 59/93] Cut WIP: Fixed crash on second "Perform cut" + some code cleaning --- src/libslic3r/Model.cpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 40 ++++++++++------------------ src/slic3r/GUI/Plater.cpp | 11 ++++---- 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d62f56105..44957cd92 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1545,7 +1545,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } - if (volume->cut_info.connector_type == CutConnectorType::Dowel) { + if (dowels && volume->cut_info.connector_type == CutConnectorType::Dowel) { // add one more solid part same as connector if this connector is a dowel ModelVolume* vol = dowels->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6b444eaec..ceb8d0b54 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3469,6 +3469,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::Seam && + m_gizmos.get_current_type() != GLGizmosManager::Cut && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); m_dirty = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 8d38bb295..481ceaa51 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -163,8 +163,6 @@ static void init_from_angle_arc(GLModel& model, double angle, double radius) //! -- -#define use_grabber_extension 1 - GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) , m_connector_type (CutConnectorType::Plug) @@ -1030,12 +1028,9 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; if (m_hover_id == Z) { -#if use_grabber_extension Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ();// some Margin rotate_vec3d_around_center(starting_box_center, Geometry::Transformation(m_rotation_m).get_rotation(), m_plane_center); -#else - const Vec3d& starting_box_center = m_plane_center; -#endif + const Vec3d& starting_drag_position = m_plane_center; double projection = 0.0; @@ -1184,14 +1179,10 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= m_c->selection_info()->get_sla_shift(); - const Vec3d rotation = Geometry::Transformation(m_rotation_m).get_rotation(); const auto move = Geometry::assemble_transform(-cut_center_offset); - const auto rot_z = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, 0, -rotation.z())); - const auto rot_y = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(0, -rotation.y(), 0)); - const auto rot_x = Geometry::assemble_transform(Vec3d::Zero(), Vec3d(-rotation.x(), 0, 0)); - const auto move2 = Geometry::assemble_transform(m_plane_center); + const auto move2 = Geometry::assemble_transform(m_plane_center); - const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * rot_x * rot_y * rot_z * move; + const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move; const Selection& selection = m_parent.get_selection(); const Selection::IndicesList& idxs = selection.get_volume_idxs(); @@ -1207,7 +1198,10 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* volume->get_instance_mirror() ); - ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * instance_matrix * volume->get_volume_transformation().get_matrix())); + auto volume_travo = instance_matrix * volume->get_volume_transformation().get_matrix(); + auto volume_offset = Geometry::Transformation(volume_travo).get_offset(); + + ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * volume_travo)); } } return ret; @@ -1223,7 +1217,7 @@ bool GLGizmoCut3D::update_bb() set_center_pos(m_bb_center + m_center_offset, true); m_radius = box.radius(); - m_grabber_connection_len = std::min(0.75 * m_radius, 35.0); + m_grabber_connection_len = 0.75 * m_radius;// std::min(0.75 * m_radius, 35.0); m_grabber_radius = m_grabber_connection_len * 0.85; m_snap_coarse_in_radius = m_grabber_radius / 3.0f; @@ -1265,8 +1259,11 @@ void GLGizmoCut3D::init_picking_models() void GLGizmoCut3D::on_render() { - if (update_bb()) + if (update_bb() || force_update_clipper_on_render) { update_clipper_on_render(); + if (force_update_clipper_on_render) + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + } init_picking_models(); @@ -1285,11 +1282,6 @@ void GLGizmoCut3D::on_render() if (!m_angle_arc.is_initialized() || m_angle != 0.0) init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); - if (force_update_clipper_on_render) { - update_clipper_on_render(); - m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); - } - render_connectors(); if (! m_connectors_editing) @@ -1725,6 +1717,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) { // update connectors pos as offset of its center before cut performing if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { + m_selected.clear(); + Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); for (CutConnector& connector : mo->cut_connectors) { connector.rotation = rotation; @@ -1736,13 +1730,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } else { // culculate shift of the connector center regarding to the position on the cut plane -#if use_grabber_extension Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); rotate_vec3d_around_center(shifted_center, rotation, m_plane_center); Vec3d norm = (shifted_center - m_plane_center).normalized(); -#else - Vec3d norm = (m_grabbers[0].center - m_plane_center).normalize(); -#endif connector.pos += norm * (0.5 * connector.height); } } @@ -1758,7 +1748,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); - m_selected.clear(); } else { // the object is SLA-elevated and the plane is under it. @@ -1871,7 +1860,6 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse m_line_beg = pt; m_line_end = pt; return true; -// volumes_trafos[i] = model_object->volumes[i]->get_matrix(); } if (cut_line_processing()) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b35b68202..0bb9e5f3d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5949,18 +5949,17 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& // suppress to call selection update for Object List to avoid call of early Gizmos on/off update p->load_model_objects(new_objects, false, false); - Selection& selection = p->get_selection(); - size_t last_id = p->model.objects.size() - 1; - for (size_t i = 0; i < new_objects.size(); ++i) - selection.add_object((unsigned int)(last_id - i), i == 0); - this->allow_snapshots(); - // now process all updates of the 3d scene update(); // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), // which is updated after a view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH) call for (size_t idx = 0; idx < p->model.objects.size(); idx++) wxGetApp().obj_list()->update_info_items(idx); + + Selection& selection = p->get_selection(); + size_t last_id = p->model.objects.size() - 1; + for (size_t i = 0; i < new_objects.size(); ++i) + selection.add_object((unsigned int)(last_id - i), i == 0); } void Plater::export_gcode(bool prefer_removable) From edebda511c732966229447309243b7fbbbaad122 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 15 Aug 2022 14:16:05 +0200 Subject: [PATCH 60/93] Cut WIP: Set Circle connector shape as a default. Allow set connectors to any place of the cut plane. --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 481ceaa51..c0611b46d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -167,7 +167,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, : GLGizmoBase(parent, icon_filename, sprite_id) , m_connector_type (CutConnectorType::Plug) , m_connector_style (size_t(CutConnectorStyle::Prizm)) - , m_connector_shape_id (size_t(CutConnectorShape::Hexagon)) + , m_connector_shape_id (size_t(CutConnectorShape::Circle)) , m_rotation_matrix(Slic3r::Matrix3d::Identity()) , m_connectors_group_id (3) { @@ -1777,7 +1777,8 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair Vec3f normal; bool clipping_plane_was_hit = false; - const Transform3d volume_trafo = get_volume_transformation(mv); +// const Transform3d volume_trafo = get_volume_transformation(mv); + const Transform3d volume_trafo = mv->get_transformation().get_matrix(); m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, instance_trafo * volume_trafo, camera, hit, normal, m_c->object_clipper()->get_clipping_plane(true), From a8919b1e915c890c45c279b831972ef07408c3ac Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 15 Aug 2022 16:12:16 +0200 Subject: [PATCH 61/93] Cut WIP: Set all dowels as a separate objects ObjectList: Don't show connectors for cut objects --- src/libslic3r/Model.cpp | 114 +++++++++++++++--------------- src/slic3r/GUI/GUI_ObjectList.cpp | 10 ++- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 44957cd92..ffa74f79c 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1446,37 +1446,26 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const cut_id.increase_check_sum(size_t(cut_obj_cnt)); } + auto clone_obj = [this](ModelObject** obj) { + (*obj) = ModelObject::new_clone(*this); + (*obj)->set_model(nullptr); + (*obj)->sla_support_points.clear(); + (*obj)->sla_drain_holes.clear(); + (*obj)->sla_points_status = sla::PointsStatus::NoPoints; + (*obj)->clear_volumes(); + (*obj)->input_file.clear(); + }; + // Clone the object to duplicate instances, materials etc. - ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr; - ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr; - ModelObject* dowels = attributes.has(ModelObjectCutAttribute::CreateDowels) ? ModelObject::new_clone(*this) : nullptr; + ModelObject* upper{ nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + clone_obj(&upper); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - upper->set_model(nullptr); - upper->sla_support_points.clear(); - upper->sla_drain_holes.clear(); - upper->sla_points_status = sla::PointsStatus::NoPoints; - upper->clear_volumes(); - upper->input_file.clear(); - } + ModelObject* lower{ nullptr }; + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + clone_obj(&lower); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - lower->set_model(nullptr); - lower->sla_support_points.clear(); - lower->sla_drain_holes.clear(); - lower->sla_points_status = sla::PointsStatus::NoPoints; - lower->clear_volumes(); - lower->input_file.clear(); - } - - if (attributes.has(ModelObjectCutAttribute::CreateDowels)) { - dowels->set_model(nullptr); - dowels->sla_support_points.clear(); - dowels->sla_drain_holes.clear(); - dowels->sla_points_status = sla::PointsStatus::NoPoints; - dowels->clear_volumes(); - dowels->input_file.clear(); - } + std::vector dowels; using namespace Geometry; @@ -1545,9 +1534,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug vol->set_type(ModelVolumeType::MODEL_PART); } - if (dowels && volume->cut_info.connector_type == CutConnectorType::Dowel) { + if (volume->cut_info.connector_type == CutConnectorType::Dowel && + attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject* dowel{ nullptr }; + // Clone the object to duplicate instances, materials etc. + clone_obj(&dowel); + // add one more solid part same as connector if this connector is a dowel - ModelVolume* vol = dowels->add_volume(*volume); + ModelVolume* vol = dowel->add_volume(*volume); vol->set_type(ModelVolumeType::MODEL_PART); // But discard rotation and Z-offset for this volume @@ -1556,6 +1550,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // Compute the displacement (in instance coordinates) to be applied to place the dowels local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); + + dowels.push_back(dowel); } } else { @@ -1704,35 +1700,37 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const res.push_back(lower); } - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels->volumes.size() > 0) { - if (!dowels->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - dowels->center_around_origin(); - dowels->translate_instances(-dowels->origin_translation); - dowels->origin_translation = Vec3d::Zero(); + if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels.size() > 0) { + for (auto dowel : dowels) { + if (!dowel->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { + dowel->center_around_origin(); + dowel->translate_instances(-dowel->origin_translation); + dowel->origin_translation = Vec3d::Zero(); + } + else { + dowel->invalidate_bounding_box(); + dowel->center_around_origin(); + } + dowel->name += "-Dowel-" + dowel->volumes[0]->name; + + // Reset instance transformation except offset and Z-rotation + for (size_t i = 0; i < instances.size(); ++i) { + auto& obj_instance = dowel->instances[i]; + const Vec3d offset = obj_instance->get_offset(); + Vec3d rotation = Vec3d::Zero(); + if (i != instance) + rotation[Z] = obj_instance->get_rotation().z(); + + const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), rotation) * local_dowels_displace; + + obj_instance->set_transformation(Geometry::Transformation()); + obj_instance->set_offset(offset + displace); + obj_instance->set_rotation(rotation); + } + + local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + res.push_back(dowel); } - else { - dowels->invalidate_bounding_box(); - dowels->center_around_origin(); - } - - dowels->name += "-Dowels"; - - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = dowels->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - Vec3d rotation = Vec3d::Zero(); - if (i != instance) - rotation[Z] = obj_instance->get_rotation().z(); - - const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), rotation) * local_dowels_displace; - - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset + displace); - obj_instance->set_rotation(rotation); - } - - res.push_back(dowels); } BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 55aab5d2d..cc6954665 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1882,9 +1882,12 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type) break; case InfoItemType::Cut: + if (0) { // #ysFIXME_Cut cnv->get_gizmos_manager().reset_all_states(); Plater::TakeSnapshot(plater, _L("Remove cut connectors")); (*m_objects)[obj_idx]->cut_connectors.clear(); + } else + Slic3r::GUI::show_error(nullptr, _L("Connectors cannot be deleted from cut object.")); break; case InfoItemType::MmuSegmentation: @@ -2583,7 +2586,7 @@ void ObjectList::part_selection_changed() } case InfoItemType::CustomSupports: case InfoItemType::CustomSeam: - case InfoItemType::Cut: +// case InfoItemType::Cut: case InfoItemType::MmuSegmentation: { GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports : @@ -2772,7 +2775,10 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio break; case InfoItemType::Cut : + if (0) // #ysFIXME_Cut should_show = !model_object->cut_connectors.empty(); + else + should_show = model_object->is_cut() && model_object->volumes.size() > 1; break; case InfoItemType::VariableLayerHeight : should_show = printer_technology() == ptFFF @@ -2827,7 +2833,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) update_info_items(obj_idx, nullptr, call_selection_changed); // add volumes to the object - if (model_object->volumes.size() > 1) { + if (model_object->volumes.size() > 1 && !model_object->is_cut()) { for (const ModelVolume* volume : model_object->volumes) { const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item, from_u8(volume->name), From 5ec84adb14a9c19f0af64feefa4da79f655b4c23 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 17 Aug 2022 09:58:20 +0200 Subject: [PATCH 62/93] Cut WIP: Add only needed modifiers --- src/libslic3r/Model.cpp | 14 +++++++------- src/slic3r/GUI/GUI_ObjectList.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ffa74f79c..293d31eae 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1380,10 +1380,6 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn void ModelObject::apply_cut_connectors(const std::string& name) { - // discard old connector markers for volumes - for (ModelVolume* volume : volumes) - volume->cut_info.discard(); - if (cut_connectors.empty()) return; @@ -1517,6 +1513,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (!volume->is_model_part()) { if (volume->cut_info.is_connector) { + volume->cut_info.discard(); + // ! Don't apply instance transformation for the conntectors. // This transformation is already there if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { @@ -1559,10 +1557,12 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // to the modifier volume transformation to preserve their shape properly. volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - // #ysFIXME - add logic for the negative volumes/connectors - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + // Some logic for the negative volumes/connectors. Add only needed modifiers + auto bb = volume->mesh().transformed_bounding_box(cut_matrix * volume->get_matrix()); + bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) + if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) lower->add_volume(*volume); } } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cc6954665..769b038fa 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2918,6 +2918,7 @@ bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i update_lock_icons_for_model(); return true; } + return false; } else if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) { type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) : @@ -2942,7 +2943,7 @@ bool ObjectList::delete_from_model_and_list(const std::vector& it if (item->type&itObject) { bool was_cut = object(item->obj_idx)->is_cut(); if (!del_object(item->obj_idx)) - continue; + return false;// continue; m_objects_model->Delete(m_objects_model->GetItemById(item->obj_idx)); if (was_cut) update_lock_icons_for_model(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 1665b8549..f8f788138 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -77,7 +77,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_keep_upper{ true }; bool m_keep_lower{ true }; bool m_place_on_cut_upper{ true }; - bool m_place_on_cut_lower{ true }; + bool m_place_on_cut_lower{ false }; bool m_rotate_upper{ false }; bool m_rotate_lower{ false }; From 70a575198b9928f0d51f5dc527fa82f075c4d94a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 17 Aug 2022 13:39:51 +0200 Subject: [PATCH 63/93] Cut WIP: Fix for drawing of the cut line --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 26 +++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index f3c15c1e5..c260959a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -212,8 +212,8 @@ std::string GLGizmoCut3D::get_tooltip() const } if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { std::string axis = m_hover_id == X ? "X" : "Y"; -// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); - return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°"); +// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); + return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°"); } return tooltip; @@ -582,7 +582,7 @@ void GLGizmoCut3D::render_cut_plane() if (!m_plane.is_initialized()) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; +// init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; init_data.reserve_vertices(4); init_data.reserve_indices(6); @@ -600,6 +600,10 @@ void GLGizmoCut3D::render_cut_plane() m_plane.init_from(std::move(init_data)); } + if (can_perform_cut()) + m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); + else + m_plane.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); m_plane.render(); glsafe(::glEnable(GL_CULL_FACE)); @@ -894,6 +898,12 @@ void GLGizmoCut3D::on_unregister_raycasters_for_picking() set_volumes_picking_state(true); } +void GLGizmoCut3D::update_raycasters_for_picking() +{ + on_unregister_raycasters_for_picking(); + on_register_raycasters_for_picking(); +} + void GLGizmoCut3D::set_volumes_picking_state(bool state) { std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); @@ -1422,7 +1432,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); if (m_imgui->button(_L("Add/Edit connectors"))) { m_connectors_editing = true; - on_unregister_raycasters_for_picking(); + update_raycasters_for_picking(); } m_imgui->disabled_end(); } @@ -1510,7 +1520,7 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); m_connectors_editing = false; - on_unregister_raycasters_for_picking(); + update_raycasters_for_picking(); std::fill(m_selected.begin(), m_selected.end(), false); m_selected_count = 0; } @@ -1826,8 +1836,7 @@ void GLGizmoCut3D::update_model_object() m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - on_unregister_raycasters_for_picking(); - on_register_raycasters_for_picking(); + update_raycasters_for_picking(); } bool GLGizmoCut3D::cut_line_processing() const @@ -1860,6 +1869,7 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse if (action == SLAGizmoEventType::LeftDown && !cut_line_processing()) { m_line_beg = pt; m_line_end = pt; + on_unregister_raycasters_for_picking(); return true; } @@ -1883,6 +1893,8 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse discard_cut_line_processing(); } + else if (action == SLAGizmoEventType::Moving) + this->set_dirty(); return true; } return false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index f8f788138..81a69e425 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -176,6 +176,7 @@ protected: virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; + void update_raycasters_for_picking(); void set_volumes_picking_state(bool state); void update_raycasters_for_picking_transform(); From cf144da4fe8dc0797096b4c352db2bde7cc0c96f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 18 Aug 2022 13:57:25 +0200 Subject: [PATCH 64/93] Cut WIP: Import/Export cut information to/from .3mf file + Fixed a crash during change object selection, when CutGizmo is On + Fixed Undo/Redo (was accidentally broken with 7912613dc8e11db2ddea8018ce16b26a01756a3b) --- src/libslic3r/Format/3mf.cpp | 110 +++++++++++++++++++++++++++ src/libslic3r/Model.hpp | 2 +- src/libslic3r/ObjectID.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 23 +++--- src/slic3r/GUI/Plater.cpp | 2 + 5 files changed, 131 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 5caff6d3a..4251110c4 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -77,6 +77,7 @@ const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt"; const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"; +const std::string CUT_INFORMATION_FILE = "Metadata/Prusa_Slicer_cut_information.xml"; static constexpr const char* MODEL_TAG = "model"; static constexpr const char* RESOURCES_TAG = "resources"; @@ -416,6 +417,7 @@ namespace Slic3r { typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; + typedef std::map IdToCutObjectIdMap; typedef std::map> IdToSlaSupportPointsMap; typedef std::map> IdToSlaDrainHolesMap; @@ -443,6 +445,7 @@ namespace Slic3r { IdToGeometryMap m_geometries; CurrentConfig m_curr_config; IdToMetadataMap m_objects_metadata; + IdToCutObjectIdMap m_cut_object_ids; IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerConfigRangesMap m_layer_config_ranges; IdToSlaSupportPointsMap m_sla_support_points; @@ -474,6 +477,7 @@ namespace Slic3r { bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions); bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); @@ -676,6 +680,10 @@ namespace Slic3r { // extract slic3r layer heights profile file _extract_layer_heights_profile_config_from_archive(archive, stat); } + else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) { + // extract slic3r layer config ranges file + _extract_cut_information_from_archive(archive, stat, config_substitutions); + } else if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE)) { // extract slic3r layer config ranges file _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions); @@ -766,6 +774,11 @@ namespace Slic3r { return false; } + // m_cut_object_ids are indexed by a 1 based model object index. + IdToCutObjectIdMap::iterator cut_object_id = m_cut_object_ids.find(object.second + 1); + if (cut_object_id != m_cut_object_ids.end()) + model_object->cut_id = std::move(cut_object_id->second); + // m_layer_heights_profiles are indexed by a 1 based model object index. IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1); if (obj_layer_heights_profile != m_layer_heights_profiles.end()) @@ -944,6 +957,48 @@ namespace Slic3r { return true; } + void _3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions) + { + if (stat.m_uncomp_size > 0) { + std::string buffer((size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) { + add_error("Error while reading cut information data to buffer"); + return; + } + + std::istringstream iss(buffer); // wrap returned xml to istringstream + pt::ptree objects_tree; + pt::read_xml(iss, objects_tree); + + for (const auto& object : objects_tree.get_child("objects")) { + pt::ptree object_tree = object.second; + int obj_idx = object_tree.get(".id", -1); + if (obj_idx <= 0) { + add_error("Found invalid object id"); + continue; + } + + IdToCutObjectIdMap::iterator object_item = m_cut_object_ids.find(obj_idx); + if (object_item != m_cut_object_ids.end()) { + add_error("Found duplicated cut_object_id"); + continue; + } + + for (const auto& obj_cut_id : object_tree) { + if (obj_cut_id.first != "cut_id") + continue; + pt::ptree cut_id_tree = obj_cut_id.second; + ObjectID obj_id(cut_id_tree.get(".id")); + CutObjectBase cut_id(ObjectID(cut_id_tree.get(".id")), + cut_id_tree.get(".check_sum"), + cut_id_tree.get(".connectors_cnt")); + m_cut_object_ids.insert({ obj_idx, std::move(cut_id) }); + } + } + } + } + void _3MF_Importer::_extract_print_config_from_archive( mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, @@ -2219,6 +2274,7 @@ namespace Slic3r { bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets); bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets); bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items); + bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model); @@ -2281,6 +2337,15 @@ namespace Slic3r { return false; } + // Adds file with information for object cut ("Metadata/Slic3r_PE_cut_information.txt"). + // All information for object cut of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model. + // The index differes from the index of an object ID of an object instance of a 3MF file! + if (!_add_cut_information_file_to_archive(archive, model)) { + close_zip_writer(&archive); + boost::filesystem::remove(filename); + return false; + } + // Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt"). // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model. // The index differes from the index of an object ID of an object instance of a 3MF file! @@ -2781,6 +2846,51 @@ namespace Slic3r { return true; } + bool _3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model) + { + std::string out = ""; + pt::ptree tree; + + unsigned int object_cnt = 0; + for (const ModelObject* object : model.objects) { + object_cnt++; + pt::ptree& obj_tree = tree.add("objects.object", ""); + + obj_tree.put(".id", object_cnt); + + // Store info for cut_id + pt::ptree& cut_id_tree = obj_tree.add("cut_id", ""); + + // store cut_id atributes + cut_id_tree.put(".id", object->cut_id.id().id); + cut_id_tree.put(".check_sum", object->cut_id.check_sum()); + cut_id_tree.put(".connectors_cnt", object->cut_id.connectors_cnt()); + } + + if (!tree.empty()) { + std::ostringstream oss; + pt::write_xml(oss, tree); + out = oss.str(); + + // Post processing("beautification") of the output string for a better preview + boost::replace_all(out, ">\n \n ", ">\n "); + boost::replace_all(out, ">", ">\n "); + // OR just + boost::replace_all(out, "><", ">\n<"); + } + + if (!out.empty()) { + if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { + add_error("Unable to add cut information file to archive"); + return false; + } + } + + return true; + } + bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model) { assert(is_decimal_separator_point()); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 9fa38ecd7..21b052337 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -351,7 +351,7 @@ public: // Holes to be drilled into the object so resin can flow out sla::DrainHoles sla_drain_holes; - // Connectors to be added into the object after cut + // Connectors to be added into the object before cut and are used to create a solid/negative volumes during a cut perform CutConnectors cut_connectors; CutObjectBase cut_id; diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 42cd21699..452f620c4 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -89,7 +89,9 @@ private: friend class cereal::access; friend class Slic3r::UndoRedo::StackImpl; template void serialize(Archive &ar) { ar(m_id); } +protected: // #vbCHECKME && #ysFIXME ObjectBase(const ObjectID id) : m_id(id) {} +private: template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } }; @@ -141,6 +143,8 @@ public: // Constructor with ignored int parameter to assign an invalid ID, to be replaced // by an existing ID copied from elsewhere. CutObjectBase(int) : ObjectBase(-1) {} + // Constructor to initialize full information from 3mf + CutObjectBase(ObjectID id, size_t check_sum, size_t connectors_cnt) : ObjectBase(id), m_check_sum(check_sum), m_connectors_cnt(connectors_cnt) {} // The class tree will have virtual tables and type information. virtual ~CutObjectBase() = default; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index c260959a1..7b6c729f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -627,7 +627,7 @@ void GLGizmoCut3D::render_cut_center_graber() const BoundingBoxf3 box = bounding_box(); - const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 6.0); + const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); double size = m_dragging && m_hover_id == Z ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); @@ -821,8 +821,8 @@ bool GLGizmoCut3D::on_init() void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) { - ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, //m_selected, - m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, + // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, m_ar_plane_center, m_ar_rotations); set_center_pos(m_ar_plane_center, true); @@ -835,8 +835,8 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const { - ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, //m_selected, - m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, + ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, + // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, m_ar_plane_center, m_ar_rotations); } @@ -1224,7 +1224,10 @@ bool GLGizmoCut3D::update_bb() m_max_pos = box.max; m_min_pos = box.min; m_bb_center = box.center(); - set_center_pos(m_bb_center + m_center_offset, true); + if (box.contains(m_center_offset)) + set_center_pos(m_bb_center + m_center_offset, true); + else + set_center_pos(m_bb_center, true); m_radius = box.radius(); m_grabber_connection_len = 0.75 * m_radius;// std::min(0.75 * m_radius, 35.0); @@ -1242,6 +1245,9 @@ bool GLGizmoCut3D::update_bb() m_circle.reset(); m_scale.reset(); m_snap_radii.reset(); + m_reference_radius.reset(); + + on_unregister_raycasters_for_picking(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { m_selected.clear(); @@ -1271,8 +1277,7 @@ void GLGizmoCut3D::on_render() { if (update_bb() || force_update_clipper_on_render) { update_clipper_on_render(); - if (force_update_clipper_on_render) - m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); } init_picking_models(); @@ -1725,11 +1730,11 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) const bool has_connectors = !mo->cut_connectors.empty(); { + Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); // update connectors pos as offset of its center before cut performing if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { m_selected.clear(); - Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); for (CutConnector& connector : mo->cut_connectors) { connector.rotation = rotation; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 27764a698..96df8892c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5962,6 +5962,8 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& // suppress to call selection update for Object List to avoid call of early Gizmos on/off update p->load_model_objects(new_objects, false, false); + this->allow_snapshots(); + // now process all updates of the 3d scene update(); // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), From a30a2547243c28dccb88de8776c8d3f26f427245 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 19 Sep 2022 11:14:29 +0200 Subject: [PATCH 65/93] After merge fix --- src/slic3r/GUI/ImGuiWrapper.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 9622da145..e9e5137be 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -56,6 +56,10 @@ static const std::map font_icons = { {ImGui::PreferencesHoverButton, "notification_preferences_hover"}, {ImGui::SliderFloatEditBtnIcon, "edit_button" }, {ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" }, + {ImGui::ExpandBtn , "expand_btn" }, + {ImGui::CollapseBtn , "collapse_btn" }, + {ImGui::RevertButton , "undo" }, + {ImGui::WarningMarkerSmall , "notification_warning" }, }; static const std::map font_icons_large = { @@ -73,10 +77,6 @@ static const std::map font_icons_large = { {ImGui::LegendShells , "legend_shells" }, {ImGui::LegendToolMarker , "legend_toolmarker" }, #endif // ENABLE_LEGEND_TOOLBAR_ICONS - {ImGui::RevertButton , "undo" }, - {ImGui::WarningMarkerSmall , "notification_warning" }, - {ImGui::ExpandBtn , "expand_btn" }, - {ImGui::CollapseBtn , "collapse_btn" }, {ImGui::CloseNotifButton , "notification_close" }, {ImGui::CloseNotifHoverButton , "notification_close_hover" }, {ImGui::EjectButton , "notification_eject_sd" }, From d7f55253cd636c228adee9a54468ef52d8f377b0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 16 Aug 2022 13:24:57 +0200 Subject: [PATCH 66/93] Cut: allow enabling/disabling an island --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 45 +++--- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 10 ++ src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 4 + src/slic3r/GUI/MeshUtils.cpp | 177 ++++++++++++----------- src/slic3r/GUI/MeshUtils.hpp | 22 ++- 6 files changed, 150 insertions(+), 110 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7b6c729f8..905e39db3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1117,7 +1117,8 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) { std::pair pos_and_normal; - if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal)) + Vec3d pos_world; + if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal, pos_world)) return; connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; update_raycasters_for_picking_transform(); @@ -1773,7 +1774,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. -bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal) +bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair& pos_and_normal, Vec3d& pos_world) { const float sla_shift = m_c->selection_info()->get_sla_shift(); @@ -1788,8 +1789,8 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair ++mesh_id; if (!mv->is_model_part()) continue; - Vec3f hit; Vec3f normal; + Vec3f hit; bool clipping_plane_was_hit = false; // const Transform3d volume_trafo = get_volume_transformation(mv); @@ -1806,6 +1807,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit_d, normal.cast()); + pos_world = hit.cast(); return true; } } @@ -1916,27 +1918,32 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - if (action == SLAGizmoEventType::LeftDown && !shift_down && m_connectors_editing) { + if (action == SLAGizmoEventType::LeftDown && !shift_down) { // If there is no selection and no hovering, add new point if (m_hover_id == -1 && !control_down && !alt_down) { std::pair pos_and_normal; - if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal)) { + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { const Vec3d& hit = pos_and_normal.first; - // The clipping plane was clicked, hit containts coordinates of the hit in world coords. - //std::cout << hit.x() << "\t" << hit.y() << "\t" << hit.z() << std::endl; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), - float(m_connector_size * 0.5), float(m_connector_depth_ratio), - float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), - CutConnectorAttributes( CutConnectorType(m_connector_type), - CutConnectorStyle(m_connector_style), - CutConnectorShape(m_connector_shape_id))); - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected.push_back(true); - assert(m_selected.size() == connectors.size()); - update_model_object(); - m_parent.set_as_dirty(); + if (m_connectors_editing) { + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); + + connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), + float(m_connector_size * 0.5), float(m_connector_depth_ratio), + float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected.push_back(true); + assert(m_selected.size() == connectors.size()); + update_model_object(); + m_parent.set_as_dirty(); + } else { + m_c->object_clipper()->pass_mouse_click(pos_world); + } return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 81a69e425..a7892a9cd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -139,7 +139,7 @@ public: GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); std::string get_tooltip() const override; - bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal); + bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal, Vec3d& pos_world); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); /// diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 97f3cb9a8..a08836c28 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -494,6 +494,16 @@ void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contou clipper->set_behaviour(fill_cut, contour_width); } +void ObjectClipper::pass_mouse_click(const Vec3d& pt) +{ + for (auto& clipper : m_clippers) + clipper->pass_mouse_click(pt); +} + +std::vector ObjectClipper::get_disabled_contours() const +{ + return std::vector(); +} void SupportsClipper::on_update() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 925408008..7c41a64b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -262,6 +262,10 @@ public: void set_position_by_ratio(double pos, bool keep_normal); void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos); void set_behavior(bool hide_clipped, bool fill_cut, double contour_width); + + void pass_mouse_click(const Vec3d& pt); + std::vector get_disabled_contours() const; + protected: diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index bbf1f7652..74fe82494 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -23,7 +23,7 @@ namespace GUI { void MeshClipper::set_behaviour(bool fill_cut, double contour_width) { if (fill_cut != m_fill_cut || contour_width != m_contour_width) - m_triangles_valid = false; + m_result.reset(); m_fill_cut = fill_cut; m_contour_width = contour_width; } @@ -34,7 +34,7 @@ void MeshClipper::set_plane(const ClippingPlane& plane) { if (m_plane != plane) { m_plane = plane; - m_triangles_valid = false; + m_result.reset(); } } @@ -43,7 +43,7 @@ void MeshClipper::set_limiting_plane(const ClippingPlane& plane) { if (m_limiting_plane != plane) { m_limiting_plane = plane; - m_triangles_valid = false; + m_result.reset(); } } @@ -53,7 +53,7 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh) { if (m_mesh != &mesh) { m_mesh = &mesh; - m_triangles_valid = false; + m_result.reset(); } } @@ -61,7 +61,7 @@ void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) { if (m_negative_mesh != &mesh) { m_negative_mesh = &mesh; - m_triangles_valid = false; + m_result.reset(); } } @@ -71,7 +71,7 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo) { if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) { m_trafo = trafo; - m_triangles_valid = false; + m_result.reset(); } } @@ -81,12 +81,9 @@ void MeshClipper::render_cut(const ColorRGBA& color) void MeshClipper::render_cut() #endif // ENABLE_LEGACY_OPENGL_REMOVAL { - if (! m_triangles_valid) + if (! m_result) recalculate_triangles(); #if ENABLE_LEGACY_OPENGL_REMOVAL - if (m_model.vertices_count() == 0 || m_model.indices_count() == 0) - return; - GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); if (curr_shader != nullptr) curr_shader->stop_using(); @@ -97,8 +94,10 @@ void MeshClipper::render_cut() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - m_model.set_color(color); - m_model.render(); + for (CutIsland& isl : m_result->cut_islands) { + isl.model.set_color(isl.disabled ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : color); + isl.model.render(); + } shader->stop_using(); } @@ -117,7 +116,7 @@ void MeshClipper::render_contour(const ColorRGBA& color) void MeshClipper::render_contour() #endif // ENABLE_LEGACY_OPENGL_REMOVAL { - if (! m_triangles_valid) + if (! m_result) recalculate_triangles(); #if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* curr_shader = wxGetApp().get_current_shader(); @@ -130,8 +129,10 @@ void MeshClipper::render_contour() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - m_model_expanded.set_color(color); - m_model_expanded.render(); + for (CutIsland& isl : m_result->cut_islands) { + isl.model_expanded.set_color(color); + isl.model_expanded.render(); + } shader->stop_using(); } @@ -145,8 +146,24 @@ void MeshClipper::render_contour() +void MeshClipper::pass_mouse_click(const Vec3d& point_in) +{ + if (! m_result || m_result->cut_islands.empty()) + return; + Vec3d point = m_result->trafo.inverse() * point_in; + Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y())); + + for (CutIsland& isl : m_result->cut_islands) { + if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) + isl.disabled = ! isl.disabled; + } +} + void MeshClipper::recalculate_triangles() { + m_result = ClipResult(); + + #if ENABLE_WORLD_COORDINATE const Transform3f instance_matrix_no_translation_no_scaling = m_trafo.get_rotation_matrix().cast(); #else @@ -176,6 +193,8 @@ void MeshClipper::recalculate_triangles() tr.rotate(q); tr = m_trafo.get_matrix() * tr; + m_result->trafo = tr; + if (m_limiting_plane != ClippingPlane::ClipsNothing()) { // Now remove whatever ended up below the limiting plane (e.g. sinking objects). @@ -231,88 +250,72 @@ void MeshClipper::recalculate_triangles() tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting - std::vector triangles2d = m_fill_cut - ? triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.) - : std::vector(); #if ENABLE_LEGACY_OPENGL_REMOVAL - m_model.reset(); + std::vector triangles2d; - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(triangles2d.size()); - init_data.reserve_indices(triangles2d.size()); + for (const ExPolygon& exp : expolys) { + triangles2d.clear(); - // vertices + indices - for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - const size_t idx = it - triangles2d.cbegin(); - init_data.add_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + m_result->cut_islands.push_back(CutIsland()); + CutIsland& isl = m_result->cut_islands.back(); + + if (m_fill_cut) { + triangles2d = triangulate_expolygon_2f(exp, m_trafo.get_matrix().matrix().determinant() < 0.); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); + + // vertices + indices + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - triangles2d.cbegin(); + init_data.add_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); + } + + if (!init_data.is_empty()) + isl.model.init_from(std::move(init_data)); + } + + if (m_contour_width != 0.) { + triangles2d.clear(); + ExPolygons expolys_exp = offset_ex(exp, scale_(m_contour_width)); + expolys_exp = diff_ex(expolys_exp, ExPolygons({exp})); + triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.); + GLModel::Geometry init_data = GLModel::Geometry(); + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(triangles2d.size()); + init_data.reserve_indices(triangles2d.size()); + + // vertices + indices + for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + const size_t idx = it - triangles2d.cbegin(); + init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); + } + + if (!init_data.is_empty()) + isl.model_expanded.init_from(std::move(init_data)); + } + + isl.expoly = std::move(exp); + isl.expoly_bb = get_extents(exp); } - - if (!init_data.is_empty()) - m_model.init_from(std::move(init_data)); #else - m_vertex_array.release_geometry(); - for (auto it=triangles2d.cbegin(); it != triangles2d.cend(); it=it+3) { - m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); - m_vertex_array.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); - m_vertex_array.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); - const size_t idx = it - triangles2d.cbegin(); - m_vertex_array.push_triangle(idx, idx+1, idx+2); - } - m_vertex_array.finalize_geometry(true); -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - - triangles2d = {}; - if (m_contour_width != 0.) { - ExPolygons expolys_exp = offset_ex(expolys, scale_(m_contour_width)); - expolys_exp = diff_ex(expolys_exp, expolys); - triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.); - } - - -#if ENABLE_LEGACY_OPENGL_REMOVAL - m_model_expanded.reset(); - - init_data = GLModel::Geometry(); - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(triangles2d.size()); - init_data.reserve_indices(triangles2d.size()); - - // vertices + indices - for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - const size_t idx = it - triangles2d.cbegin(); - init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); - //if (init_data./*format.*/index_type == GLModel::Geometry::EIndexType::USHORT) - // init_data.add_ushort_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); - //else - // init_data.add_uint_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2); - } - - if (!init_data.is_empty()) - m_model_expanded.init_from(std::move(init_data)); -#else - m_vertex_array_expanded.release_geometry(); - for (auto it=triangles2d.cbegin(); it != triangles2d.cend(); it=it+3) { - m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up); - m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+1))(0), (*(it+1))(1), height_mesh), up); - m_vertex_array_expanded.push_geometry(tr * Vec3d((*(it+2))(0), (*(it+2))(1), height_mesh), up); - const size_t idx = it - triangles2d.cbegin(); - m_vertex_array_expanded.push_triangle(idx, idx+1, idx+2); - } - m_vertex_array_expanded.finalize_geometry(true); + #error NOT IMPLEMENTED #endif // ENABLE_LEGACY_OPENGL_REMOVAL - m_triangles_valid = true; +#if ENABLE_LEGACY_OPENGL_REMOVAL +#else + #error NOT IMPLEMENTED +#endif // ENABLE_LEGACY_OPENGL_REMOVAL } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 5df4236a6..726f228ca 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -14,6 +14,7 @@ #endif // ENABLE_LEGACY_OPENGL_REMOVAL #include +#include #if ENABLE_RAYCAST_PICKING #include #endif // ENABLE_RAYCAST_PICKING @@ -112,6 +113,9 @@ public: void render_cut(); #endif // ENABLE_LEGACY_OPENGL_REMOVAL + void pass_mouse_click(const Vec3d& pt); + + private: void recalculate_triangles(); @@ -121,12 +125,24 @@ private: ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); #if ENABLE_LEGACY_OPENGL_REMOVAL - GLModel m_model; - GLModel m_model_expanded; + + struct CutIsland { + GLModel model; + GLModel model_expanded; + ExPolygon expoly; + BoundingBox expoly_bb; + bool disabled = false; + }; + struct ClipResult { + std::vector cut_islands; + Transform3d trafo; // this rotates the cut into world coords + }; + std::optional m_result; + #else + #error NOT IMLEMENTED GLIndexedVertexArray m_vertex_array; #endif // ENABLE_LEGACY_OPENGL_REMOVAL - bool m_triangles_valid = false; bool m_fill_cut = true; double m_contour_width = 0.; }; From 582eccd51bc93542a73eb9d0aac9924c88c3064c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 19 Sep 2022 10:58:20 +0200 Subject: [PATCH 67/93] Cut: turn off contour disabling for now --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 905e39db3..d235ad4c5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1942,7 +1942,9 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi update_model_object(); m_parent.set_as_dirty(); } else { - m_c->object_clipper()->pass_mouse_click(pos_world); + // Following would inform the clipper about the mouse click, so it can + // toggle the respective contour as disabled. + //m_c->object_clipper()->pass_mouse_click(pos_world); } return true; From 75f3d1bddbc5d39eaf360c63113ab0a0c8ba04cc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 19 Sep 2022 10:56:32 +0200 Subject: [PATCH 68/93] Cut: fix cutting plane when object is anisotropically scaled --- src/slic3r/GUI/MeshUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 74fe82494..e7c1d0fe9 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -177,7 +177,7 @@ void MeshClipper::recalculate_triangles() // Now do the cutting MeshSlicingParams slicing_params; - slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up, Vec3d::UnitZ())); + slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up_noscale.cast(), Vec3d::UnitZ())); ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params)); @@ -188,7 +188,7 @@ void MeshClipper::recalculate_triangles() // Triangulate and rotate the cut into world coords: Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), up); + q.setFromTwoVectors(Vec3d::UnitZ(), up_noscale.cast()); Transform3d tr = Transform3d::Identity(); tr.rotate(q); tr = m_trafo.get_matrix() * tr; From e93ff4d087ff048c32f5d09e85810b9e15bed9a0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 19 Sep 2022 17:53:01 +0200 Subject: [PATCH 69/93] WIP Cut: Fixed transformation of a cut plane and a clipper. + Fixed a picking of the scaled grabbers + Code refactoring --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 684 +++++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 23 +- 2 files changed, 355 insertions(+), 352 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d235ad4c5..9e1183c07 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -4,11 +4,6 @@ #include -#include -#include -#include -#include - #include #include "slic3r/GUI/GUI_App.hpp" @@ -16,7 +11,6 @@ #include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "libslic3r/AppConfig.hpp" -#include "libslic3r/Model.hpp" #include "libslic3r/TriangleMeshSlicer.hpp" #include "imgui/imgui_internal.h" @@ -34,6 +28,8 @@ const unsigned int ScaleLongEvery = 2; const float ScaleLongTooth = 0.1f; // in percent of radius const unsigned int SnapRegionsCount = 8; +using namespace Geometry; + // Generates mesh for a line static GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) { @@ -51,6 +47,26 @@ static GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos) return init_data; } +// Generates mesh for a square plane +static GLModel::Geometry its_make_square_plane(float radius) +{ + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.reserve_vertices(4); + init_data.reserve_indices(6); + + // vertices + init_data.add_vertex(Vec3f(-radius, -radius, 0.0)); + init_data.add_vertex(Vec3f(radius , -radius, 0.0)); + init_data.add_vertex(Vec3f(radius , radius , 0.0)); + init_data.add_vertex(Vec3f(-radius, radius , 0.0)); + + // indices + init_data.add_triangle(0, 1, 2); + init_data.add_triangle(2, 3, 0); + return init_data; +} + //! -- #ysFIXME those functions bodies are ported from GizmoRotation // Generates mesh for a circle static void init_from_circle(GLModel& model, double radius) @@ -63,7 +79,7 @@ static void init_from_circle(GLModel& model, double radius) // vertices + indices for (unsigned int i = 0; i < ScaleStepsCount; ++i) { const float angle = float(i * ScaleStepRad); - init_data.add_vertex(Vec3f(::cos(angle) * radius, ::sin(angle) * radius, 0.0f)); + init_data.add_vertex(Vec3f(::cos(angle) * float(radius), ::sin(angle) * float(radius), 0.0f)); init_data.add_index(i); } @@ -74,8 +90,8 @@ static void init_from_circle(GLModel& model, double radius) // Generates mesh for a scale static void init_from_scale(GLModel& model, double radius) { - const float out_radius_long = radius * (1.0f + ScaleLongTooth); - const float out_radius_short = radius * (1.0f + 0.5f * ScaleLongTooth); + const float out_radius_long = float(radius) * (1.0f + ScaleLongTooth); + const float out_radius_short = float(radius) * (1.0f + 0.5f * ScaleLongTooth); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; @@ -87,8 +103,8 @@ static void init_from_scale(GLModel& model, double radius) const float angle = float(i * ScaleStepRad); const float cosa = ::cos(angle); const float sina = ::sin(angle); - const float in_x = cosa * radius; - const float in_y = sina * radius; + const float in_x = cosa * float(radius); + const float in_y = sina * float(radius); const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; @@ -108,7 +124,7 @@ static void init_from_scale(GLModel& model, double radius) static void init_from_snap_radii(GLModel& model, double radius) { const float step = 2.0f * float(PI) / float(SnapRegionsCount); - const float in_radius = radius / 3.0f; + const float in_radius = float(radius) / 3.0f; const float out_radius = 2.0f * in_radius; GLModel::Geometry init_data; @@ -118,7 +134,7 @@ static void init_from_snap_radii(GLModel& model, double radius) // vertices + indices for (unsigned int i = 0; i < ScaleStepsCount; ++i) { - const float angle = float(i * step); + const float angle = float(i) * step; const float cosa = ::cos(angle); const float sina = ::sin(angle); const float in_x = cosa * in_radius; @@ -144,7 +160,7 @@ static void init_from_angle_arc(GLModel& model, double angle, double radius) model.reset(); const float step_angle = float(angle) / float(AngleResolution); - const float ex_radius = radius; + const float ex_radius = float(radius); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; @@ -165,11 +181,10 @@ static void init_from_angle_arc(GLModel& model, double angle, double radius) GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) + , m_connectors_group_id (3) , m_connector_type (CutConnectorType::Plug) , m_connector_style (size_t(CutConnectorStyle::Prizm)) , m_connector_shape_id (size_t(CutConnectorShape::Circle)) - , m_rotation_matrix(Slic3r::Matrix3d::Identity()) - , m_connectors_group_id (3) { m_modes = { _u8L("Planar")//, _u8L("Grid") // , _u8L("Radial"), _u8L("Modular") @@ -212,8 +227,7 @@ std::string GLGizmoCut3D::get_tooltip() const } if (tooltip.empty() && (m_hover_id == X || m_hover_id == Y)) { std::string axis = m_hover_id == X ? "X" : "Y"; -// return axis + ": " + format(float(Geometry::rad2deg(Geometry::Transformation(m_rotation_m).get_rotation()[m_hover_id])), 1) + _u8L("°"); - return axis + ": " + format(float(Geometry::rad2deg(m_angle)), 1) + _u8L("°"); + return axis + ": " + format(float(rad2deg(m_angle)), 1) + _u8L("°"); } return tooltip; @@ -310,18 +324,9 @@ void GLGizmoCut3D::shift_cut_z(double delta) set_center(new_cut_center); } -void GLGizmoCut3D::rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center) +void GLGizmoCut3D::rotate_vec3d_around_plane_center(Vec3d&vec) { - if (m_rotations != angles) { - m_rotation_matrix = Eigen::AngleAxisd(angles[Z], Vec3d::UnitZ()) - * Eigen::AngleAxisd(angles[Y], Vec3d::UnitY()) - * Eigen::AngleAxisd(angles[X], Vec3d::UnitX()); - m_rotations = angles; - } - - vec -= center; - vec = m_rotation_matrix * vec; - vec += center; + vec = Transformation( assemble_transform(m_plane_center) * m_rotation_m * assemble_transform(-m_plane_center)).get_matrix() * vec; } void GLGizmoCut3D::put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset) @@ -345,17 +350,14 @@ void GLGizmoCut3D::put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_ void GLGizmoCut3D::update_clipper() { - const Vec3d angles = Geometry::Transformation(m_rotation_m).get_rotation(); BoundingBoxf3 box = bounding_box(); - double radius = box.radius(); - Vec3d beg, end = beg = m_plane_center; - beg[Z] = box.center().z() - radius;//box.min.z(); - end[Z] = box.center().z() + radius;//box.max.z(); + beg[Z] = box.center().z() - m_radius; + end[Z] = box.center().z() + m_radius; - rotate_vec3d_around_center(beg, angles, m_plane_center); - rotate_vec3d_around_center(end, angles, m_plane_center); + rotate_vec3d_around_plane_center(beg); + rotate_vec3d_around_plane_center(end); double dist = (m_plane_center - beg).norm(); @@ -460,20 +462,20 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, double& float value = (float)value_in; if (m_imperial_units) - value *= ObjectManipulation::mm_to_in; + value *= float(ObjectManipulation::mm_to_in); float old_val = value; const BoundingBoxf3 bbox = bounding_box(); float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); float min_size = 1.f; if (m_imperial_units) { - mean_size *= ObjectManipulation::mm_to_in; - min_size *= ObjectManipulation::mm_to_in; + mean_size *= float(ObjectManipulation::mm_to_in); + min_size *= float(ObjectManipulation::mm_to_in); } std::string format = m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); m_imgui->slider_float(("##" + label).c_str(), &value, min_size, mean_size, format.c_str()); - value_in = (double)(value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0)); + value_in = (double)(value) * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); ImGui::SameLine(m_label_width + m_control_width + 3); ImGui::PushItemWidth(m_control_width * 0.3f); @@ -490,13 +492,13 @@ void GLGizmoCut3D::render_move_center_input(int axis) ImGui::AlignTextToFramePadding(); m_imgui->text(m_axis_names[axis]+":"); ImGui::SameLine(); - ImGui::PushItemWidth(0.3*m_control_width); + ImGui::PushItemWidth(0.3f*m_control_width); Vec3d move = m_plane_center; double in_val, value = in_val = move[axis]; if (m_imperial_units) value *= ObjectManipulation::mm_to_in; - ImGui::InputDouble(("##move_" + m_axis_names[axis]).c_str(), &value, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal); + ImGui::InputDouble(("##move_" + m_axis_names[axis]).c_str(), &value, 0.0, 0.0, "%.2f", ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); double val = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); @@ -570,36 +572,11 @@ void GLGizmoCut3D::render_cut_plane() shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( - m_plane_center, - Geometry::Transformation(m_rotation_m).get_rotation(), - Vec3d::Ones(), - Vec3d::Ones() - ); + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (!m_plane.is_initialized()) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; -// init_data.color = { 0.8f, 0.8f, 0.8f, 0.5f }; - init_data.reserve_vertices(4); - init_data.reserve_indices(6); - - // vertices - float radius = (float)bounding_box().radius(); - init_data.add_vertex(Vec3f(-radius, -radius, 0.0)); - init_data.add_vertex(Vec3f( radius, -radius, 0.0)); - init_data.add_vertex(Vec3f( radius, radius, 0.0)); - init_data.add_vertex(Vec3f(-radius, radius, 0.0)); - - // indices - init_data.add_triangle(0, 1, 2); - init_data.add_triangle(2, 3, 0); - - m_plane.init_from(std::move(init_data)); - } - if (can_perform_cut()) m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); else @@ -612,7 +589,12 @@ void GLGizmoCut3D::render_cut_plane() shader->stop_using(); } -void GLGizmoCut3D::render_cut_center_graber() +static float get_grabber_mean_size(const BoundingBoxf3& bb) +{ + return float((bb.size().x() + bb.size().y() + bb.size().z()) / 3.0); +} + +void GLGizmoCut3D::render_cut_center_grabber() { glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); @@ -623,12 +605,12 @@ void GLGizmoCut3D::render_cut_center_graber() ColorRGBA color = m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; const Camera& camera = wxGetApp().plater()->get_camera(); - const Grabber& graber = m_grabbers.front(); + const Grabber& grabber = m_grabbers.front(); const BoundingBoxf3 box = bounding_box(); - const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 3.0); - double size = m_dragging && m_hover_id == Z ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + const float mean_size = get_grabber_mean_size(box); + double size = m_dragging && m_hover_id == Z ? double(grabber.get_dragging_half_size(mean_size)) : double(grabber.get_half_size(mean_size)); Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); Vec3d offset = 1.25 * size * Vec3d::UnitZ(); @@ -637,7 +619,7 @@ void GLGizmoCut3D::render_cut_center_graber() shader->set_uniform("emission_factor", 0.1f); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - const Transform3d view_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_rotation_m; + const Transform3d view_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; auto render = [shader, this](GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix) { shader->set_uniform("view_model_matrix", view_model_matrix); @@ -657,7 +639,7 @@ void GLGizmoCut3D::render_cut_center_graber() line_shader->set_uniform("emission_factor", 0.1f); line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - const Transform3d trafo = view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, m_grabber_connection_len)); + const Transform3d trafo = view_matrix * assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, m_grabber_connection_len)); line_shader->set_uniform("view_model_matrix", trafo); line_shader->set_uniform("normal_matrix", (Matrix3d)trafo.matrix().block(0, 0, 3, 3).inverse().transpose()); line_shader->set_uniform("width", 0.2f); @@ -674,9 +656,9 @@ void GLGizmoCut3D::render_cut_center_graber() Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_start_dragging_m; if (axis == X) - view_model_matrix = view_model_matrix * Geometry::rotation_transform(0.5 * PI * Vec3d::UnitY()) * Geometry::rotation_transform(-PI * Vec3d::UnitZ()); + view_model_matrix = view_model_matrix * rotation_transform( 0.5 * PI * Vec3d::UnitY()) * rotation_transform(-PI * Vec3d::UnitZ()); else - view_model_matrix = view_model_matrix * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * Geometry::rotation_transform(-0.5 * PI * Vec3d::UnitY()); + view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * rotation_transform(-0.5 * PI * Vec3d::UnitY()); shader->stop_using(); @@ -710,33 +692,33 @@ void GLGizmoCut3D::render_cut_center_graber() if ((!m_dragging && m_hover_id < 0)) render_grabber_connection(color); - render(m_sphere.model, color, view_matrix * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + render(m_sphere.model, color, view_matrix * assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); if (!m_dragging && m_hover_id < 0 || m_hover_id == Z) { const BoundingBoxf3 tbb = transformed_bounding_box(); if (tbb.min.z() <= 0.0) - render(m_cone.model, color, view_matrix * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); + render(m_cone.model, color, view_matrix * assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); if (tbb.max.z() >= 0.0) - render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, Vec3d::Zero(), cone_scale)); + render(m_cone.model, color, view_matrix * assemble_transform(offset, Vec3d::Zero(), cone_scale)); } // render top sphere for X/Y grabbers if ((!m_dragging && m_hover_id < 0) || m_hover_id == X || m_hover_id == Y) { - size = m_dragging ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + size = m_dragging ? double(grabber.get_dragging_half_size(mean_size)) : double(grabber.get_half_size(mean_size)); color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY(); - render(m_sphere.model, color, view_matrix * Geometry::assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); + render(m_sphere.model, color, view_matrix * assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); } // render X grabber if ((!m_dragging && m_hover_id < 0) || m_hover_id == X) { - size = m_dragging && m_hover_id == X ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + size = m_dragging && m_hover_id == X ? double(grabber.get_dragging_half_size(mean_size)) : double(grabber.get_half_size(mean_size)); cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); @@ -746,16 +728,16 @@ void GLGizmoCut3D::render_cut_center_graber() } offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); + render(m_cone.model, color, view_matrix * assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); + render(m_cone.model, color, view_matrix * assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); } // render Y grabber if ((!m_dragging && m_hover_id < 0) || m_hover_id == Y) { - size = m_dragging && m_hover_id == Y ? double(graber.get_dragging_half_size(mean_size)) : double(graber.get_half_size(mean_size)); + size = m_dragging && m_hover_id == Y ? double(grabber.get_dragging_half_size(mean_size)) : double(grabber.get_half_size(mean_size)); cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); @@ -765,9 +747,9 @@ void GLGizmoCut3D::render_cut_center_graber() } offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); + render(m_cone.model, color, view_matrix * assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); + render(m_cone.model, color, view_matrix * assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); } shader->stop_using(); @@ -826,7 +808,7 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) m_ar_plane_center, m_ar_rotations); set_center_pos(m_ar_plane_center, true); - m_rotation_m = Geometry::rotation_transform(m_ar_rotations); + m_rotation_m = rotation_transform(m_ar_rotations); force_update_clipper_on_render = true; @@ -852,7 +834,7 @@ void GLGizmoCut3D::on_set_state() // initiate archived values m_ar_plane_center = m_plane_center; - m_ar_rotations = Geometry::Transformation(m_rotation_m).get_rotation(); + m_ar_rotations = Transformation(m_rotation_m).get_rotation(); m_parent.request_extra_frame(); } @@ -872,7 +854,7 @@ void GLGizmoCut3D::on_register_raycasters_for_picking() if (m_connectors_editing) { if (CommonGizmosDataObjects::SelectionInfo* si = m_c->selection_info()) { const CutConnectors& connectors = si->model_object()->cut_connectors; - for (size_t i = 0; i < connectors.size(); ++i) + for (int i = 0; i < int(connectors.size()); ++i) m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i + m_connectors_group_id, *(m_shapes[connectors[i].attribs]).mesh_raycaster, Transform3d::Identity())); } } @@ -952,36 +934,36 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() } pos[Z] += sla_shift; - m_raycasters[i]->set_transform(Geometry::assemble_transform( + m_raycasters[i]->set_transform(assemble_transform( pos, - Geometry::Transformation(m_rotation_m).get_rotation(), + Transformation(m_rotation_m).get_rotation(), Vec3d(connector.radius, connector.radius, height) )); } } else { - const Transform3d trafo = Geometry::translation_transform(m_plane_center) * m_rotation_m; + const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m; const BoundingBoxf3 box = bounding_box(); - const double mean_size = float((box.size().x() + box.size().y() + box.size().z()) / 6.0); + const float mean_size = get_grabber_mean_size(box); double size = double(m_grabbers.front().get_half_size(mean_size)); Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); Vec3d offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); - m_raycasters[0]->set_transform(trafo * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), scale)); + m_raycasters[0]->set_transform(trafo * assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), scale)); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); - m_raycasters[1]->set_transform(trafo * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), scale)); + m_raycasters[1]->set_transform(trafo * assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), scale)); offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); - m_raycasters[2]->set_transform(trafo * Geometry::assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), scale)); + m_raycasters[2]->set_transform(trafo * assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); - m_raycasters[3]->set_transform(trafo * Geometry::assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), scale)); + m_raycasters[3]->set_transform(trafo * assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), scale)); offset = 1.25 * size * Vec3d::UnitZ(); - m_raycasters[4]->set_transform(trafo * Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); - m_raycasters[5]->set_transform(trafo * Geometry::assemble_transform(-offset, PI * Vec3d::UnitX(), scale)); - m_raycasters[6]->set_transform(trafo * Geometry::assemble_transform(offset, Vec3d::Zero(), scale)); + m_raycasters[4]->set_transform(trafo * assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + m_raycasters[5]->set_transform(trafo * assemble_transform(-offset, PI * Vec3d::UnitX(), scale)); + m_raycasters[6]->set_transform(trafo * assemble_transform(offset, Vec3d::Zero(), scale)); } } @@ -1039,7 +1021,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) if (m_hover_id == Z) { Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ();// some Margin - rotate_vec3d_around_center(starting_box_center, Geometry::Transformation(m_rotation_m).get_rotation(), m_plane_center); + rotate_vec3d_around_plane_center(starting_box_center); const Vec3d& starting_drag_position = m_plane_center; double projection = 0.0; @@ -1103,7 +1085,7 @@ void GLGizmoCut3D::on_dragging(const UpdateData& data) theta += 0.5 * PI; rotation[m_hover_id] = theta; - m_rotation_m = m_start_dragging_m * Geometry::rotation_transform(rotation); + m_rotation_m = m_start_dragging_m * rotation_transform(rotation); m_angle = theta; while (m_angle > two_pi) @@ -1141,7 +1123,7 @@ void GLGizmoCut3D::on_stop_dragging() m_angle_arc.reset(); m_angle = 0.0; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); - m_ar_rotations = Geometry::Transformation(m_rotation_m).get_rotation(); + m_ar_rotations = Transformation(m_rotation_m).get_rotation(); m_start_dragging_m = m_rotation_m; } @@ -1190,8 +1172,8 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= m_c->selection_info()->get_sla_shift(); - const auto move = Geometry::assemble_transform(-cut_center_offset); - const auto move2 = Geometry::assemble_transform(m_plane_center); + const auto move = assemble_transform(-cut_center_offset); + const auto move2 = assemble_transform(m_plane_center); const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move; @@ -1202,17 +1184,16 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* // respect just to the solid parts for FFF and ignore pad and supports for SLA if (!volume->is_modifier && !volume->is_sla_pad() && !volume->is_sla_support()) { - const auto instance_matrix = Geometry::assemble_transform( + const auto instance_matrix = assemble_transform( Vec3d::Zero(), // don't apply offset volume->get_instance_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), volume->get_instance_scaling_factor(), volume->get_instance_mirror() ); - auto volume_travo = instance_matrix * volume->get_volume_transformation().get_matrix(); - auto volume_offset = Geometry::Transformation(volume_travo).get_offset(); + auto volume_trafo = instance_matrix * volume->get_volume_transformation().get_matrix(); - ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * volume_travo)); + ret.merge(volume->transformed_convex_hull_bounding_box(cut_matrix * volume_trafo)); } } return ret; @@ -1274,15 +1255,8 @@ void GLGizmoCut3D::init_picking_models() } } -void GLGizmoCut3D::on_render() +void GLGizmoCut3D::init_rendering_items() { - if (update_bb() || force_update_clipper_on_render) { - update_clipper_on_render(); - m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); - } - - init_picking_models(); - if (!m_grabber_connection.is_initialized()) m_grabber_connection.init_from(its_make_line(Vec3f::Zero(), Vec3f::UnitZ())); if (!m_circle.is_initialized()) @@ -1298,6 +1272,21 @@ void GLGizmoCut3D::on_render() if (!m_angle_arc.is_initialized() || m_angle != 0.0) init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); + if (!m_plane.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) + m_plane.init_from(its_make_square_plane(float(m_radius))); +} + +void GLGizmoCut3D::on_render() +{ + if (update_bb() || force_update_clipper_on_render) { + update_clipper_on_render(); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + } + + init_picking_models(); + + init_rendering_items(); + render_connectors(); if (! m_connectors_editing) @@ -1308,27 +1297,41 @@ void GLGizmoCut3D::on_render() if (!m_hide_cut_plane && !m_connectors_editing) { render_cut_plane(); - render_cut_center_graber(); + render_cut_center_grabber(); } render_cut_line(); } -void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) +void GLGizmoCut3D::render_debug_block() +{ + m_imgui->begin(wxString("DEBUG")); + + static bool hide_clipped = false; + static bool fill_cut = false; + static float contour_width = 0.4f; + + m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); + if (m_imgui->checkbox("hide_clipped", hide_clipped) && !hide_clipped) + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); + m_imgui->checkbox("fill_cut", fill_cut); + m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); + if (auto oc = m_c->object_clipper()) + oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width)); + + m_imgui->end(); +} + +void GLGizmoCut3D::adjust_window_position(float x, float y, float bottom_limit) { static float last_y = 0.0f; static float last_h = 0.0f; - m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - m_label_width = m_imgui->get_style_scaling() * 100.0f; - m_control_width = m_imgui->get_style_scaling() * 150.0f; - - // adjust window position to avoid overlap the view toolbar const float win_h = ImGui::GetWindowHeight(); - y = std::min(y, bottom_limit - win_h); + y = std::min(y, bottom_limit - win_h); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); + if (last_h != win_h || last_y != y) { // ask canvas for another frame to render the window in the correct position m_imgui->set_requires_extra_frame(); @@ -1337,247 +1340,244 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) if (last_y != y) last_y = y; } +} +void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) +{ + // add shortcuts panel + if (m_imgui->button("? " + (m_show_shortcuts ? wxString(ImGui::CollapseBtn) : wxString(ImGui::ExpandBtn)))) + m_show_shortcuts = !m_show_shortcuts; + + if (m_show_shortcuts) + for (const auto&shortcut : m_shortcuts ){ + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, shortcut.first); + ImGui::SameLine(m_label_width); + m_imgui->text(shortcut.second); + } + + // Connectors section + + ImGui::Separator(); + + // WIP : Auto : Need to implement + // m_imgui->text(_L("Mode")); + // render_connect_mode_radio_button(CutConnectorMode::Auto); + // render_connect_mode_radio_button(CutConnectorMode::Manual); + + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); + + m_imgui->disabled_begin(connectors.empty()); + ImGui::SameLine(m_label_width); + if (m_imgui->button(" " + _L("Reset") + " ##connectors")) + reset_connectors(); + m_imgui->disabled_end(); + + m_imgui->text(_L("Type")); + bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); + type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); + if (type_changed) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.type = CutConnectorType(m_connector_type); + + if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.style = CutConnectorStyle(m_connector_style) ; + + if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) + connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); + + if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) + for (size_t idx = 0; idx < m_selected.size() ; idx++) + if (m_selected[idx]) { + auto&connector = connectors[idx]; + connector.height = float(m_connector_depth_ratio); + connector.height_tolerance = 0.01f * float(m_connector_depth_ratio_tolerance); + } + + if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + auto&connector = connectors[idx]; + connector.radius = float(m_connector_size * 0.5); + connector.radius_tolerance = 0.01f * float(m_connector_size_tolerance); + } + + if (m_imgui->button(_L("Confirm connectors"))) { + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); + m_connectors_editing = false; + update_raycasters_for_picking(); + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; + } + + m_parent.request_extra_frame(); +} + +void GLGizmoCut3D::render_cut_plane_editing(CutConnectors &connectors) +{ + // WIP : cut plane mode // render_combo(_u8L("Mode"), m_modes, m_mode); - CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - bool cut_clicked = false; - bool revert_move{ false }; - bool revert_rotation{ false }; - bool fff_printer = wxGetApp().plater()->printer_technology() == ptFFF; + if (m_mode == size_t(CutMode::cutPlanar)) { + ImGui::AlignTextToFramePadding(); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); + ImGui::Separator(); - if (! m_connectors_editing) { - if (m_mode == size_t(CutMode::cutPlanar)) { - ImGui::AlignTextToFramePadding(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); - ImGui::Separator(); - - //////// - double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); - const BoundingBoxf3 tbb = transformed_bounding_box(); - double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; - double bottom = (tbb.max.z() <= 0.0 ? tbb.size().z() : (tbb.min.z() * (-1))) * koef; + double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); + const BoundingBoxf3 tbb = transformed_bounding_box(); - Vec3d tbb_sz = tbb.size(); - wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + - ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + - ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + Vec3d tbb_sz = tbb.size(); + wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + + ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + + ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Build size")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build size")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); - //static float v = 0.; // TODO: connect to cutting plane position - m_imgui->text(_L("Cut position: ")); - render_move_center_input(Z); - //m_imgui->input_double(unit_str, v); - //v = std::clamp(v, 0.f, float(bottom+top)); - if (m_imgui->button("Reset cutting plane")) { - set_center(bounding_box().center()); - m_rotation_m = Transform3d::Identity(); - m_angle_arc.reset(); - update_clipper(); - } + m_imgui->text(_L("Cut position: ")); + render_move_center_input(Z); + + if (m_imgui->button("Reset cutting plane")) { + set_center(bounding_box().center()); + m_rotation_m = Transform3d::Identity(); + m_angle_arc.reset(); + update_clipper(); } - if (m_mode == size_t(CutMode::cutPlanar)) { - ImGui::Separator(); - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut") + ": "); - bool keep = true; - - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Upper part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); - m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); - - m_imgui->disabled_begin(!m_keep_upper); - - m_imgui->disabled_begin(is_approx(Geometry::Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Geometry::Transformation(m_rotation_m).get_rotation().y(), 0.)); - if (m_imgui->checkbox(_L("Place on cut") + "##upper", m_place_on_cut_upper)) - m_rotate_upper = false; - m_imgui->disabled_end(); - - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper)) - m_place_on_cut_upper = false; - - m_imgui->disabled_end(); - - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Lower part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - - m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); - m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); - m_imgui->disabled_begin(!m_keep_lower); - - if (m_imgui->checkbox(_L("Place on cut") + "##lower", m_place_on_cut_lower)) - m_rotate_lower = false; - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower)) - m_place_on_cut_lower = false; - - m_imgui->disabled_end(); - } - - if (fff_printer) { - ImGui::Separator(); - - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) { - m_connectors_editing = true; - update_raycasters_for_picking(); - } - m_imgui->disabled_end(); - } - } - else { // connectors mode - if (m_imgui->button("? " + (m_show_shortcuts ? wxString(ImGui::CollapseBtn) : wxString(ImGui::ExpandBtn)))) - m_show_shortcuts = !m_show_shortcuts; - - if (m_show_shortcuts) - for (const auto& shortcut : m_shortcuts ){ - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, shortcut.first); - ImGui::SameLine(m_label_width); - m_imgui->text(shortcut.second); - } - - m_imgui->disabled_begin(!m_keep_lower || !m_keep_upper); - // Connectors section ImGui::Separator(); ImGui::AlignTextToFramePadding(); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors")); + m_imgui->text(_L("After cut") + ": "); + bool keep = true; - m_imgui->disabled_begin(connectors.empty()); ImGui::SameLine(m_label_width); - if (m_imgui->button(" " + _L("Reset") + " ##connectors")) - reset_connectors(); + m_imgui->text(_L("Upper part")); + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); m_imgui->disabled_end(); -/* - m_imgui->text(_L("Mode")); - render_connect_mode_radio_button(CutConnectorMode::Auto); - render_connect_mode_radio_button(CutConnectorMode::Manual); -*/ + ImGui::SameLine(3 * m_label_width); - if (m_selected_count == 1) - for (size_t idx = 0; idx < m_selected.size(); idx++) - if (m_selected[idx]) { - auto& connector = connectors[idx]; - m_connector_depth_ratio = connector.height; - m_connector_depth_ratio_tolerance = 100 * connector.height_tolerance; - m_connector_size = 2. * connector.radius; - m_connector_size_tolerance = 100 * connector.radius_tolerance; - m_connector_type = connector.attribs.type; - m_connector_style = size_t(connector.attribs.style); - m_connector_shape_id = size_t(connector.attribs.shape); + m_imgui->disabled_begin(!m_keep_upper); - break; - } + m_imgui->disabled_begin(is_approx(Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Transformation(m_rotation_m).get_rotation().y(), 0.)); + if (m_imgui->checkbox(_L("Place on cut") + "##upper", m_place_on_cut_upper)) + m_rotate_upper = false; + m_imgui->disabled_end(); - m_imgui->text(_L("Type")); - bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); - type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); - if (type_changed) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.type = CutConnectorType(m_connector_type); - - if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.style = CutConnectorStyle(m_connector_style) ; - - if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); - - if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) { - auto& connector = connectors[idx]; - connector.height = float(m_connector_depth_ratio); - connector.height_tolerance = 0.01f * m_connector_depth_ratio_tolerance; - } - - if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) - for (size_t idx = 0; idx < m_selected.size(); idx++) - if (m_selected[idx]) { - auto& connector = connectors[idx]; - connector.radius = float(m_connector_size * 0.5); - connector.radius_tolerance = 0.01f * m_connector_size_tolerance; - } + ImGui::SameLine(); + if (m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper)) + m_place_on_cut_upper = false; m_imgui->disabled_end(); - if (m_imgui->button(_L("Confirm connectors"))) { - m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); - m_connectors_editing = false; + m_imgui->text(""); + ImGui::SameLine(m_label_width); + m_imgui->text(_L("Lower part")); + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + + m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); + m_imgui->disabled_end(); + ImGui::SameLine(3 * m_label_width); + m_imgui->disabled_begin(!m_keep_lower); + + if (m_imgui->checkbox(_L("Place on cut") + "##lower", m_place_on_cut_lower)) + m_rotate_lower = false; + ImGui::SameLine(); + if (m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower)) + m_place_on_cut_lower = false; + + m_imgui->disabled_end(); + } + + if (wxGetApp().plater()->printer_technology() == ptFFF) { + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); + if (m_imgui->button(_L("Add/Edit connectors"))) { + m_connectors_editing = true; update_raycasters_for_picking(); - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; } - m_parent.request_extra_frame(); + m_imgui->disabled_end(); } ImGui::Separator(); - if (fff_printer) + m_imgui->disabled_begin(!can_perform_cut()); + if((m_keep_upper || m_keep_lower) && m_imgui->button(_L("Perform cut"))) + perform_cut(m_parent.get_selection());; + m_imgui->disabled_end(); +} + +void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) +{ + m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; + m_label_width = m_imgui->get_style_scaling() * 100.0f; + m_control_width = m_imgui->get_style_scaling() * 150.0f; + + if (m_selected_count == 1) + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) { + auto&connector = connectors[idx]; + m_connector_depth_ratio = connector.height; + m_connector_depth_ratio_tolerance = 100 * connector.height_tolerance; + m_connector_size = 2. * connector.radius; + m_connector_size_tolerance = 100 * connector.radius_tolerance; + m_connector_type = connector.attribs.type; + m_connector_style = size_t(connector.attribs.style); + m_connector_shape_id = size_t(connector.attribs.shape); + + break; + } +} + +void GLGizmoCut3D::render_input_window_warning() const +{ + if (wxGetApp().plater()->printer_technology() == ptFFF) m_imgui->text(m_has_invalid_connector ? wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.") : wxString()); - if (!m_connectors_editing) { - m_imgui->disabled_begin(!can_perform_cut()); - cut_clicked = m_imgui->button(_L("Perform cut")); - m_imgui->disabled_end(); - } + if (!m_keep_upper && !m_keep_lower) + m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Invalid state. \nNo one part is selected for keep after cut")); +} + +void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) +{ + m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + // adjust window position to avoid overlap the view toolbar + adjust_window_position(x, y, bottom_limit); + + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + + init_input_window_data(connectors); + + if (m_connectors_editing) // connectors mode + render_connectors_editing(connectors); + else + render_cut_plane_editing(connectors); + + render_input_window_warning(); m_imgui->end(); - //////// - m_imgui->begin(wxString("DEBUG")); - static bool hide_clipped = false; - static bool fill_cut = false; - static float contour_width = 0.4f; - m_imgui->checkbox(_L("Hide cut plane and grabbers"), m_hide_cut_plane); - if (m_imgui->checkbox("hide_clipped", hide_clipped) && !hide_clipped) - m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); - m_imgui->checkbox("fill_cut", fill_cut); - m_imgui->slider_float("contour_width", &contour_width, 0.f, 3.f); - m_c->object_clipper()->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, contour_width); - m_imgui->end(); - //////// - - if (cut_clicked && (m_keep_upper || m_keep_lower)) - perform_cut(m_parent.get_selection()); - - if (revert_move) - set_center(bounding_box().center()); - if (revert_rotation) { - m_rotation_m = Transform3d::Identity(); - m_angle_arc.reset(); - update_clipper(); - } + render_debug_block(); } // get volume transformation regarding to the "border". Border is related from the size of connectors Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) const { bool is_prizm_dowel = m_connector_type == CutConnectorType::Dowel && m_connector_style == size_t(CutConnectorStyle::Prizm); - const Transform3d connector_trafo = Geometry::assemble_transform( + const Transform3d connector_trafo = assemble_transform( is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(), - Geometry::Transformation(m_rotation_m).get_rotation(), + Transformation(m_rotation_m).get_rotation(), Vec3d(0.5*m_connector_size, 0.5*m_connector_size, is_prizm_dowel ? 2 * m_connector_depth_ratio : m_connector_depth_ratio), Vec3d::Ones()); const Vec3d connector_bb = m_connector_mesh.transformed_bounding_box(connector_trafo).size(); @@ -1593,7 +1593,7 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c const Vec3d offset(vol_trans.x() * border_scale.x(), vol_trans.y() * border_scale.y(), vol_trans.z() * border_scale.z()); // scale and translate volume to suppress to put connectors too close to the border - return Geometry::assemble_transform(offset, Vec3d::Zero(), Vec3d::Ones() - border_scale, Vec3d::Ones()) * vol_matrix; + return assemble_transform(offset, Vec3d::Zero(), Vec3d::Ones() - border_scale, Vec3d::Ones()) * vol_matrix; } void GLGizmoCut3D::render_connectors() @@ -1636,7 +1636,7 @@ void GLGizmoCut3D::render_connectors() const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; - const Transform3d instance_trafo = Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix(); + const Transform3d instance_trafo = assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix(); m_has_invalid_connector = false; @@ -1682,9 +1682,9 @@ void GLGizmoCut3D::render_connectors() m_shapes[connector.attribs].model.set_color(render_color); - const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform( + const Transform3d view_model_matrix = camera.get_view_matrix() * assemble_transform( pos, - Geometry::Transformation(m_rotation_m).get_rotation(), + Transformation(m_rotation_m).get_rotation(), Vec3d(connector.radius, connector.radius, height) ); shader->set_uniform("view_model_matrix", view_model_matrix); @@ -1727,7 +1727,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) if(!mo) return; - Vec3d rotation = Geometry::Transformation(m_rotation_m).get_rotation(); + Vec3d rotation = Transformation(m_rotation_m).get_rotation(); const bool has_connectors = !mo->cut_connectors.empty(); { @@ -1747,7 +1747,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) else { // culculate shift of the connector center regarding to the position on the cut plane Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); - rotate_vec3d_around_center(shifted_center, rotation, m_plane_center); + rotate_vec3d_around_plane_center(shifted_center); Vec3d norm = (shifted_center - m_plane_center).normalized(); connector.pos += norm * (0.5 * connector.height); } @@ -1781,7 +1781,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; const Transform3d instance_trafo = sla_shift > 0.0 ? - Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); + assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); int mesh_id = -1; @@ -1861,8 +1861,8 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse const float sla_shift = m_c->selection_info()->get_sla_shift(); const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; - Transform3d inst_trafo = sla_shift > 0.0 ? - Geometry::assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : + Transform3d inst_trafo = sla_shift > 0.f ? + assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); @@ -1930,7 +1930,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, Geometry::Transformation(m_rotation_m).get_rotation(), + connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), float(m_connector_size * 0.5), float(m_connector_depth_ratio), float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), CutConnectorAttributes( CutConnectorType(m_connector_type), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index a7892a9cd..11c7b1abd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -2,11 +2,9 @@ #define slic3r_GLGizmoCut_hpp_ #include "GLGizmoBase.hpp" -#include "GLGizmoRotate.hpp" -#include "GLGizmoMove.hpp" #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" -#include "libslic3r/ObjectID.hpp" +#include "libslic3r/Model.hpp" namespace Slic3r { @@ -100,9 +98,6 @@ class GLGizmoCut3D : public GLGizmoBase bool m_has_invalid_connector{ false }; - Matrix3d m_rotation_matrix; - Vec3d m_rotations{ Vec3d::Zero() }; - bool m_show_shortcuts{ false }; std::vector> m_shortcuts; @@ -150,7 +145,7 @@ public: bool on_mouse(const wxMouseEvent &mouse_event) override; void shift_cut_z(double delta); - void rotate_vec3d_around_center(Vec3d& vec, const Vec3d& angles, const Vec3d& center); + void rotate_vec3d_around_plane_center(Vec3d&vec); void put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset); void update_clipper(); void update_clipper_on_render(); @@ -174,6 +169,13 @@ protected: void on_stop_dragging() override; void on_render() override; + void render_debug_block(); + void adjust_window_position(float x, float y, float bottom_limit); + void render_connectors_editing(CutConnectors &connectors); + void render_cut_plane_editing(CutConnectors &connectors); + void init_input_window_data(CutConnectors &connectors); + void render_input_window_warning() const; + virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; void update_raycasters_for_picking(); @@ -204,12 +206,13 @@ private: void discard_cut_line_processing(); void render_cut_plane(); - void render_cut_center_graber(); + void render_cut_center_grabber(); void render_cut_line(); - void perform_cut(const Selection& selection); - void set_center_pos(const Vec3d& center_pos, bool force = false); + void perform_cut(const Selection&selection); + void set_center_pos(const Vec3d¢er_pos, bool force = false); bool update_bb(); void init_picking_models(); + void init_rendering_items(); void reset_connectors(); void update_connector_shape(); void update_model_object(); From 94685b5ad89386f1677185b461c4bb68a9b9d457 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 20 Sep 2022 16:38:24 +0200 Subject: [PATCH 70/93] WIP Cut: Fixed an adding/deleting of the connectors to the selection_info + more code refactoring --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 602 ++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 45 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 +- 3 files changed, 356 insertions(+), 293 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9e1183c07..2adebbbec 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -254,10 +254,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) if (use_grabbers(mouse_event)) { if (m_hover_id >= m_connectors_group_id) { - if (mouse_event.LeftDown()) { - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; - } + if (mouse_event.LeftDown() && !mouse_event.CmdDown()&& !mouse_event.AltDown()) + unselect_all_connectors(); if (mouse_event.LeftUp() && !mouse_event.ShiftDown()) gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), mouse_event.CmdDown()); } @@ -1012,99 +1010,103 @@ Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse return transform(mouse_ray, m).intersect_plane(0.0); } +void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) +{ + Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ(); // some Margin + rotate_vec3d_around_plane_center(starting_box_center); + + const Vec3d&starting_drag_position = m_plane_center; + double projection = 0.0; + + Vec3d starting_vec = starting_drag_position - starting_box_center; + if (starting_vec.norm() != 0.0) { + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + // vector from the starting position to the found intersection + Vec3d inters_vec = inters - starting_drag_position; + + starting_vec.normalize(); + // finds projection of the vector along the staring direction + projection = inters_vec.dot(starting_vec); + } + if (wxGetKeyState(WXK_SHIFT)) + projection = m_snap_step * (double)std::round(projection / m_snap_step); + + const Vec3d shift = starting_vec * projection; + + // move cut plane center + set_center(m_plane_center + shift); +} + +void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) +{ + const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray)); + + const Vec2d orig_dir = Vec2d::UnitX(); + const Vec2d new_dir = mouse_pos.normalized(); + + const double two_pi = 2.0 * PI; + + double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); + if (cross2(orig_dir, new_dir) < 0.0) + theta = two_pi - theta; + + const double len = mouse_pos.norm(); + // snap to coarse snap region + if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { + const double step = two_pi / double(SnapRegionsCount); + theta = step * std::round(theta / step); + } + // snap to fine snap region (scale) + else if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { + const double step = two_pi / double(ScaleStepsCount); + theta = step * std::round(theta / step); + } + + if (is_approx(theta, two_pi)) + theta = 0.0; + if (m_hover_id == X) + theta += 0.5 * PI; + + Vec3d rotation = Vec3d::Zero(); + rotation[m_hover_id] = theta; + m_rotation_m = m_start_dragging_m * rotation_transform(rotation); + + m_angle = theta; + while (m_angle > two_pi) + m_angle -= two_pi; + if (m_angle < 0.0) + m_angle += two_pi; + + update_clipper(); +} + +void GLGizmoCut3D::dragging_connector(const GLGizmoBase::UpdateData &data) +{ + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; + std::pair pos_and_normal; + Vec3d pos_world; + + if (unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal, pos_world)) { + connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; + update_raycasters_for_picking_transform(); + } +} + void GLGizmoCut3D::on_dragging(const UpdateData& data) { if (m_hover_id < 0) return; - - CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - - if (m_hover_id == Z) { - Vec3d starting_box_center = m_plane_center - Vec3d::UnitZ();// some Margin - rotate_vec3d_around_plane_center(starting_box_center); - - const Vec3d& starting_drag_position = m_plane_center; - double projection = 0.0; - - Vec3d starting_vec = starting_drag_position - starting_box_center; - if (starting_vec.norm() != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; - - starting_vec.normalize(); - // finds projection of the vector along the staring direction - projection = inters_vec.dot(starting_vec); - } - if (wxGetKeyState(WXK_SHIFT)) - projection = m_snap_step * (double)std::round(projection / m_snap_step); - - Vec3d shift = starting_vec * projection; - - // move cut plane center - set_center(m_plane_center + shift); - } - - else if (m_hover_id == X || m_hover_id == Y) { - - Vec3d rotation = Vec3d::Zero(); - - const Vec2d mouse_pos = to_2d(mouse_position_in_local_plane((Axis)m_hover_id, data.mouse_ray)); - - const Vec2d orig_dir = Vec2d::UnitX(); - const Vec2d new_dir = mouse_pos.normalized(); - - const double two_pi = 2.0 * PI; - - double theta = ::acos(std::clamp(new_dir.dot(orig_dir), -1.0, 1.0)); - if (cross2(orig_dir, new_dir) < 0.0) - theta = two_pi - theta; - - const double len = mouse_pos.norm(); - // snap to coarse snap region - if (m_snap_coarse_in_radius <= len && len <= m_snap_coarse_out_radius) { - const double step = two_pi / double(SnapRegionsCount); - theta = step * std::round(theta / step); - } - else { - // snap to fine snap region (scale) - if (m_snap_fine_in_radius <= len && len <= m_snap_fine_out_radius) { - const double step = two_pi / double(ScaleStepsCount); - theta = step * std::round(theta / step); - } - } - - if (theta == two_pi) - theta = 0.0; - if (m_hover_id == X) - theta += 0.5 * PI; - - rotation[m_hover_id] = theta; - m_rotation_m = m_start_dragging_m * rotation_transform(rotation); - - m_angle = theta; - while (m_angle > two_pi) - m_angle -= two_pi; - if (m_angle < 0.0) - m_angle += two_pi; - - update_clipper(); - } - + if (m_hover_id == Z) + dragging_grabber_z(data); + else if (m_hover_id == X || m_hover_id == Y) + dragging_grabber_xy(data); else if (m_hover_id >= m_connectors_group_id && m_connector_mode == CutConnectorMode::Manual) - { - std::pair pos_and_normal; - Vec3d pos_world; - if (!unproject_on_cut_plane(data.mouse_pos.cast(), pos_and_normal, pos_world)) - return; - connectors[m_hover_id - m_connectors_group_id].pos = pos_and_normal.first; - update_raycasters_for_picking_transform(); - } + dragging_connector(data); } void GLGizmoCut3D::on_start_dragging() @@ -1215,10 +1217,10 @@ bool GLGizmoCut3D::update_bb() m_grabber_connection_len = 0.75 * m_radius;// std::min(0.75 * m_radius, 35.0); m_grabber_radius = m_grabber_connection_len * 0.85; - m_snap_coarse_in_radius = m_grabber_radius / 3.0f; - m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius; + m_snap_coarse_in_radius = m_grabber_radius / 3.0; + m_snap_coarse_out_radius = m_snap_coarse_in_radius * 2.; m_snap_fine_in_radius = m_grabber_connection_len * 0.85; - m_snap_fine_out_radius = m_grabber_connection_len * 1.15f; + m_snap_fine_out_radius = m_grabber_connection_len * 1.15; m_plane.reset(); m_cone.reset(); @@ -1276,11 +1278,20 @@ void GLGizmoCut3D::init_rendering_items() m_plane.init_from(its_make_square_plane(float(m_radius))); } +void GLGizmoCut3D::render_clipper_cut() +{ + if (! m_connectors_editing) + ::glDisable(GL_DEPTH_TEST); + m_c->object_clipper()->render_cut(); + if (! m_connectors_editing) + ::glEnable(GL_DEPTH_TEST); +} + void GLGizmoCut3D::on_render() { if (update_bb() || force_update_clipper_on_render) { update_clipper_on_render(); - m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4f); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, 0.4); } init_picking_models(); @@ -1289,11 +1300,7 @@ void GLGizmoCut3D::on_render() render_connectors(); - if (! m_connectors_editing) - ::glDisable(GL_DEPTH_TEST); - m_c->object_clipper()->render_cut(); - if (! m_connectors_editing) - ::glEnable(GL_DEPTH_TEST); + render_clipper_cut(); if (!m_hide_cut_plane && !m_connectors_editing) { render_cut_plane(); @@ -1303,7 +1310,7 @@ void GLGizmoCut3D::on_render() render_cut_line(); } -void GLGizmoCut3D::render_debug_block() +void GLGizmoCut3D::render_debug_input_window() { m_imgui->begin(wxString("DEBUG")); @@ -1342,9 +1349,20 @@ void GLGizmoCut3D::adjust_window_position(float x, float y, float bottom_limit) } } -void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) +void GLGizmoCut3D::unselect_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), false); + m_selected_count = 0; +} + +void GLGizmoCut3D::select_all_connectors() +{ + std::fill(m_selected.begin(), m_selected.end(), true); + m_selected_count = int(m_selected.size()); +} + +void GLGizmoCut3D::render_shortcuts() { - // add shortcuts panel if (m_imgui->button("? " + (m_show_shortcuts ? wxString(ImGui::CollapseBtn) : wxString(ImGui::ExpandBtn)))) m_show_shortcuts = !m_show_shortcuts; @@ -1354,6 +1372,19 @@ void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) ImGui::SameLine(m_label_width); m_imgui->text(shortcut.second); } +} + +void GLGizmoCut3D::apply_selected_connectors(std::function apply_fn) +{ + for (size_t idx = 0; idx < m_selected.size(); idx++) + if (m_selected[idx]) + apply_fn(idx); +} + +void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) +{ + // add shortcuts panel + render_shortcuts(); // Connectors section @@ -1377,48 +1408,67 @@ void GLGizmoCut3D::render_connectors_editing(CutConnectors &connectors) bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug); type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel); if (type_changed) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.type = CutConnectorType(m_connector_type); + apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.style = CutConnectorStyle(m_connector_style) ; + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) - connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) - for (size_t idx = 0; idx < m_selected.size() ; idx++) - if (m_selected[idx]) { - auto&connector = connectors[idx]; - connector.height = float(m_connector_depth_ratio); - connector.height_tolerance = 0.01f * float(m_connector_depth_ratio_tolerance); - } + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].height = float(m_connector_depth_ratio); + connectors[idx].height_tolerance = 0.01f * float(m_connector_depth_ratio_tolerance); + }); if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) - for (size_t idx = 0; idx < m_selected.size(); idx++) - if (m_selected[idx]) { - auto&connector = connectors[idx]; - connector.radius = float(m_connector_size * 0.5); - connector.radius_tolerance = 0.01f * float(m_connector_size_tolerance); - } + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].radius = float(m_connector_size * 0.5); + connectors[idx].radius_tolerance = 0.01f * float(m_connector_size_tolerance); + }); if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); - m_connectors_editing = false; - update_raycasters_for_picking(); - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; + unselect_all_connectors(); + set_connectors_editing(false); } m_parent.request_extra_frame(); } -void GLGizmoCut3D::render_cut_plane_editing(CutConnectors &connectors) +void GLGizmoCut3D::render_build_size() +{ + double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; + std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); + const BoundingBoxf3 tbb = transformed_bounding_box(); + + Vec3d tbb_sz = tbb.size(); + wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + + ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + + ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Build size")); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); +} + +void GLGizmoCut3D::reset_cut_plane() +{ + set_center(bounding_box().center()); + m_rotation_m = Transform3d::Identity(); + m_angle_arc.reset(); + update_clipper(); +} + +void GLGizmoCut3D::set_connectors_editing(bool connectors_editing) +{ + m_connectors_editing = connectors_editing; + update_raycasters_for_picking(); +} + +void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) { // WIP : cut plane mode // render_combo(_u8L("Mode"), m_modes, m_mode); @@ -1426,95 +1476,60 @@ void GLGizmoCut3D::render_cut_plane_editing(CutConnectors &connectors) if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::AlignTextToFramePadding(); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); + ImGui::Separator(); - double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); - const BoundingBoxf3 tbb = transformed_bounding_box(); - - Vec3d tbb_sz = tbb.size(); - wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + - ", Y: " + double_to_string(tbb_sz.y() * koef, 2) + unit_str + - ", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str; + render_build_size(); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Build size")); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size); - m_imgui->text(_L("Cut position: ")); render_move_center_input(Z); - if (m_imgui->button("Reset cutting plane")) { - set_center(bounding_box().center()); - m_rotation_m = Transform3d::Identity(); - m_angle_arc.reset(); - update_clipper(); - } + if (m_imgui->button("Reset cutting plane")) + reset_cut_plane(); ImGui::Separator(); + auto render_part_action_line = [this, connectors](const wxString& info_label, const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { + bool keep = true; + + m_imgui->text(info_label); + ImGui::SameLine(m_label_width); + m_imgui->text(label); + + ImGui::SameLine(2 * m_label_width); + + m_imgui->disabled_begin(!connectors.empty()); + m_imgui->checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); + m_imgui->disabled_end(); + + ImGui::SameLine(3 * m_label_width); + + m_imgui->disabled_begin(!keep_part); + if (m_imgui->checkbox(_L("Place on cut") + suffix, place_on_cut_part)) + rotate_part = false; + ImGui::SameLine(); + if (m_imgui->checkbox(_L("Flip") + suffix, rotate_part)) + place_on_cut_part = false; + m_imgui->disabled_end(); + }; + ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("After cut") + ": "); - bool keep = true; - - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Upper part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - m_imgui->checkbox(_L("Keep") + "##upper", connectors.empty() ? m_keep_upper : keep); - m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); - - m_imgui->disabled_begin(!m_keep_upper); - - m_imgui->disabled_begin(is_approx(Transformation(m_rotation_m).get_rotation().x(), 0.) && is_approx(Transformation(m_rotation_m).get_rotation().y(), 0.)); - if (m_imgui->checkbox(_L("Place on cut") + "##upper", m_place_on_cut_upper)) - m_rotate_upper = false; - m_imgui->disabled_end(); - - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + "##upper", m_rotate_upper)) - m_place_on_cut_upper = false; - - m_imgui->disabled_end(); - - m_imgui->text(""); - ImGui::SameLine(m_label_width); - m_imgui->text(_L("Lower part")); - ImGui::SameLine(2 * m_label_width); - - m_imgui->disabled_begin(!connectors.empty()); - - m_imgui->checkbox(_L("Keep") + "##lower", connectors.empty() ? m_keep_lower : keep); - m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); - m_imgui->disabled_begin(!m_keep_lower); - - if (m_imgui->checkbox(_L("Place on cut") + "##lower", m_place_on_cut_lower)) - m_rotate_lower = false; - ImGui::SameLine(); - if (m_imgui->checkbox(_L("Flip") + "##lower", m_rotate_lower)) - m_place_on_cut_lower = false; - - m_imgui->disabled_end(); + render_part_action_line(_L("After cut") + ": ", _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); + render_part_action_line("", _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); } if (wxGetApp().plater()->printer_technology() == ptFFF) { m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) { - m_connectors_editing = true; - update_raycasters_for_picking(); - } + if (m_imgui->button(_L("Add/Edit connectors"))) + set_connectors_editing(true); m_imgui->disabled_end(); } ImGui::Separator(); m_imgui->disabled_begin(!can_perform_cut()); - if((m_keep_upper || m_keep_lower) && m_imgui->button(_L("Perform cut"))) - perform_cut(m_parent.get_selection());; + if(m_imgui->button(_L("Perform cut"))) + perform_cut(m_parent.get_selection());; m_imgui->disabled_end(); } @@ -1550,7 +1565,7 @@ void GLGizmoCut3D::render_input_window_warning() const void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) { - m_imgui->begin(_L("Cut"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar adjust_window_position(x, y, bottom_limit); @@ -1560,15 +1575,15 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) init_input_window_data(connectors); if (m_connectors_editing) // connectors mode - render_connectors_editing(connectors); + render_connectors_input_window(connectors); else - render_cut_plane_editing(connectors); + render_cut_plane_input_window(connectors); render_input_window_warning(); m_imgui->end(); - render_debug_block(); + render_debug_input_window(); } // get volume transformation regarding to the "border". Border is related from the size of connectors @@ -1598,9 +1613,6 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c void GLGizmoCut3D::render_connectors() { - if (!m_connectors_editing) - return; - ::glEnable(GL_DEPTH_TEST); if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) @@ -1631,7 +1643,7 @@ void GLGizmoCut3D::render_connectors() const ModelInstance* mi = mo->instances[inst_id]; const Vec3d& instance_offset = mi->get_offset(); - const float sla_shift = m_c->selection_info()->get_sla_shift(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; @@ -1643,7 +1655,7 @@ void GLGizmoCut3D::render_connectors() for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; - double height = connector.height; + double height = double(connector.height); // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; if (connector.attribs.type == CutConnectorType::Dowel && @@ -1757,13 +1769,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) } plater->cut(object_idx, instance_idx, cut_center_offset, rotation, - only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | - only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | - only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | - only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | - only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | - only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | - only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); + only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | + only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | + only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | + only_if(m_place_on_cut_lower, ModelObjectCutAttribute::PlaceOnCutLower) | + only_if(m_rotate_upper, ModelObjectCutAttribute::FlipUpper) | + only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | + only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); } else { // the object is SLA-elevated and the plane is under it. @@ -1907,6 +1919,85 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse return false; } +bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_position) +{ + std::pair pos_and_normal; + Vec3d pos_world; + if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { + const Vec3d& hit = pos_and_normal.first; + + if (m_connectors_editing) { + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); + + connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), + float(m_connector_size) * 0.5f, float(m_connector_depth_ratio), + float(m_connector_size_tolerance) * 0.01f, float(m_connector_depth_ratio_tolerance) * 0.01f, + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); + unselect_all_connectors(); + m_selected.push_back(true); + assert(m_selected.size() == connectors.size()); + update_model_object(); + m_parent.set_as_dirty(); + } + else { + // Following would inform the clipper about the mouse click, so it can + // toggle the respective contour as disabled. + //m_c->object_clipper()->pass_mouse_click(pos_world); + } + + return true; + } + return false; +} + +bool GLGizmoCut3D::delete_selected_connectors(CutConnectors& connectors) +{ + if (connectors.empty()) + return false; + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector"), UndoRedo::SnapshotType::GizmoAction); + + // remove connectors + for (int i = int(connectors.size()) - 1; i >= 0; i--) + if (m_selected[i]) + connectors.erase(connectors.begin() + i); + // remove selections + m_selected.erase(std::remove_if(m_selected.begin(), m_selected.end(), [](const auto& selected) { + return selected; }), m_selected.end()); + + assert(m_selected.size() == connectors.size()); + update_model_object(); + m_parent.set_as_dirty(); + return true; +} + +void GLGizmoCut3D::select_connector(int idx, bool select) +{ + m_selected[idx] = select; + if (select) + ++m_selected_count; + else + --m_selected_count; +} + +bool GLGizmoCut3D::is_selection_changed(bool alt_down, bool control_down) +{ + if (m_hover_id >= m_connectors_group_id) { + if (alt_down) + select_connector(m_hover_id - m_connectors_group_id, false); + else { + if (!control_down) + unselect_all_connectors(); + select_connector(m_hover_id - m_connectors_group_id, true); + } + return true; + } + return false; +} + bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) @@ -1920,76 +2011,33 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (action == SLAGizmoEventType::LeftDown && !shift_down) { // If there is no selection and no hovering, add new point - if (m_hover_id == -1 && !control_down && !alt_down) { - std::pair pos_and_normal; - Vec3d pos_world; - if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { - const Vec3d& hit = pos_and_normal.first; - - if (m_connectors_editing) { - - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - - connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), - float(m_connector_size * 0.5), float(m_connector_depth_ratio), - float(0.01f * m_connector_size_tolerance), float(0.01f * m_connector_depth_ratio_tolerance), - CutConnectorAttributes( CutConnectorType(m_connector_type), - CutConnectorStyle(m_connector_style), - CutConnectorShape(m_connector_shape_id))); - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected.push_back(true); - assert(m_selected.size() == connectors.size()); - update_model_object(); - m_parent.set_as_dirty(); - } else { - // Following would inform the clipper about the mouse click, so it can - // toggle the respective contour as disabled. - //m_c->object_clipper()->pass_mouse_click(pos_world); - } - - return true; - } - return false; - } + if (m_hover_id == -1 && !control_down && !alt_down) + return add_connector(connectors, mouse_position); return true; } - else if (action == SLAGizmoEventType::LeftUp && !shift_down && m_connectors_editing) { - if (m_hover_id >= m_connectors_group_id) { - if (alt_down) { - m_selected[m_hover_id - m_connectors_group_id] = false; - --m_selected_count; - } - else { - if (!control_down) { - std::fill(m_selected.begin(), m_selected.end(), false); - m_selected_count = 0; - } - m_selected[m_hover_id - m_connectors_group_id] = true; - ++m_selected_count; - } - return true; - } - } - else if (action == SLAGizmoEventType::RightDown && !shift_down && m_connectors_editing) { + if (!m_connectors_editing) + return false; + + if (action == SLAGizmoEventType::LeftUp && !shift_down) + return is_selection_changed(alt_down, control_down); + + if (action == SLAGizmoEventType::RightDown && !shift_down) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: if (m_hover_id < m_connectors_group_id) return false; + unselect_all_connectors(); + select_connector(m_hover_id - m_connectors_group_id, true); + return delete_selected_connectors(connectors); + } + + if (action == SLAGizmoEventType::Delete) + return delete_selected_connectors(connectors); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete connector"), UndoRedo::SnapshotType::GizmoAction); - - size_t connector_id = m_hover_id - m_connectors_group_id; - connectors.erase(connectors.begin() + connector_id); - m_selected.erase(m_selected.begin() + connector_id); - assert(m_selected.size() == connectors.size()); - update_model_object(); - m_parent.set_as_dirty(); - - return true; - } - else if (action == SLAGizmoEventType::SelectAll) { - std::fill(m_selected.begin(), m_selected.end(), true); + if (action == SLAGizmoEventType::SelectAll) { + select_all_connectors(); return true; } + return false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 11c7b1abd..ac3113a0a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -155,26 +155,40 @@ public: BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const; protected: - bool on_init() override; - void on_load(cereal::BinaryInputArchive& ar) override; - void on_save(cereal::BinaryOutputArchive& ar) const override; - std::string on_get_name() const override; - void on_set_state() override; + bool on_init() override; + void on_load(cereal::BinaryInputArchive&ar) override; + void on_save(cereal::BinaryOutputArchive&ar) const override; + std::string on_get_name() const override; + void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; - void on_set_hover_id() override; - bool on_is_activable() const override; - Vec3d mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const; - void on_dragging(const UpdateData& data) override; - void on_start_dragging() override; - void on_stop_dragging() override; - void on_render() override; + void on_set_hover_id() override; + bool on_is_activable() const override; + Vec3d mouse_position_in_local_plane(Axis axis, const Linef3&mouse_ray) const; + void dragging_grabber_z(const GLGizmoBase::UpdateData &data); + void dragging_grabber_xy(const GLGizmoBase::UpdateData &data); + void dragging_connector(const GLGizmoBase::UpdateData &data); + void on_dragging(const UpdateData&data) override; + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render() override; - void render_debug_block(); + void render_debug_input_window(); void adjust_window_position(float x, float y, float bottom_limit); - void render_connectors_editing(CutConnectors &connectors); - void render_cut_plane_editing(CutConnectors &connectors); + void unselect_all_connectors(); + void select_all_connectors(); + void render_shortcuts(); + void apply_selected_connectors(std::function apply_fn); + void render_connectors_input_window(CutConnectors &connectors); + void render_build_size(); + void reset_cut_plane(); + void set_connectors_editing(bool connectors_editing); + void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); void render_input_window_warning() const; + bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position); + bool delete_selected_connectors(CutConnectors&connectors); + void select_connector(int idx, bool select); + bool is_selection_changed(bool alt_down, bool control_down); virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; @@ -213,6 +227,7 @@ private: bool update_bb(); void init_picking_models(); void init_rendering_items(); + void render_clipper_cut(); void reset_connectors(); void update_connector_shape(); void update_model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 024b23819..3946e54e1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -551,7 +551,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case WXK_BACK: case WXK_DELETE: { - if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete)) + if ((m_current == SlaSupports || m_current == Hollow || m_current == Cut) && gizmo_event(SLAGizmoEventType::Delete)) processed = true; break; From a6f94193d5282a6cd0726e487128ccaab07146b9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 20 Sep 2022 21:25:02 +0200 Subject: [PATCH 71/93] Cut: Fix transformations, make contour not scale with object --- src/libslic3r/ExPolygon.cpp | 7 +++++ src/libslic3r/ExPolygon.hpp | 1 + src/slic3r/GUI/MeshUtils.cpp | 60 ++++++++++++++++++++++++++---------- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index 7e22127cd..4de88101c 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -19,6 +19,13 @@ void ExPolygon::scale(double factor) hole.scale(factor); } +void ExPolygon::scale(double factor_x, double factor_y) +{ + contour.scale(factor_x, factor_y); + for (Polygon &hole : holes) + hole.scale(factor_x, factor_y); +} + void ExPolygon::translate(const Point &p) { contour.translate(p); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 3e42c8670..d13056f95 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -36,6 +36,7 @@ public: void clear() { contour.points.clear(); holes.clear(); } void scale(double factor); + void scale(double factor_x, double factor_y); void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); } void translate(const Point &vector); void rotate(double angle); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index e7c1d0fe9..b6f95eead 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -16,6 +16,8 @@ #include +#include + namespace Slic3r { namespace GUI { @@ -163,21 +165,13 @@ void MeshClipper::recalculate_triangles() { m_result = ClipResult(); - -#if ENABLE_WORLD_COORDINATE - const Transform3f instance_matrix_no_translation_no_scaling = m_trafo.get_rotation_matrix().cast(); -#else - const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast(); -#endif // ENABLE_WORLD_COORDINATE - // Calculate clipping plane normal in mesh coordinates. - const Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast(); - const Vec3d up = up_noscale.cast().cwiseProduct(m_trafo.get_scaling_factor()); - // Calculate distance from mesh origin to the clipping plane (in mesh coordinates). - const float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm()); + auto plane_mesh = Eigen::Hyperplane(m_plane.get_normal(), -m_plane.distance(Vec3d::Zero())).transform(m_trafo.get_matrix().inverse()); + const Vec3d up = plane_mesh.normal(); + const float height_mesh = -plane_mesh.offset(); // Now do the cutting MeshSlicingParams slicing_params; - slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up_noscale.cast(), Vec3d::UnitZ())); + slicing_params.trafo.rotate(Eigen::Quaternion::FromTwoVectors(up, Vec3d::UnitZ())); ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params)); @@ -188,7 +182,7 @@ void MeshClipper::recalculate_triangles() // Triangulate and rotate the cut into world coords: Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d::UnitZ(), up_noscale.cast()); + q.setFromTwoVectors(Vec3d::UnitZ(), up); Transform3d tr = Transform3d::Identity(); tr.rotate(q); tr = m_trafo.get_matrix() * tr; @@ -240,7 +234,7 @@ void MeshClipper::recalculate_triangles() // it so it lies on our line. This will be the figure to subtract // from the cut. The coordinates must not overflow after the transform, // make the rectangle a bit smaller. - const coord_t size = (std::numeric_limits::max() - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4; + const coord_t size = (std::numeric_limits::max()/2 - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4; Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; ep.front().rotate(angle); ep.front().translate(scale_(-e * a), scale_(-e * b)); @@ -280,10 +274,42 @@ void MeshClipper::recalculate_triangles() isl.model.init_from(std::move(init_data)); } - if (m_contour_width != 0.) { + if (m_contour_width != 0. && ! exp.contour.empty()) { triangles2d.clear(); - ExPolygons expolys_exp = offset_ex(exp, scale_(m_contour_width)); - expolys_exp = diff_ex(expolys_exp, ExPolygons({exp})); + + // The contours must not scale with the object. Check the scale factor + // in the respective directions, create a scaled copy of the ExPolygon + // offset it and then unscale the result again. + + Transform3d t = tr; + t.translation() = Vec3d::Zero(); + double scale_x = (t * Vec3d::UnitX()).norm(); + double scale_y = (t * Vec3d::UnitY()).norm(); + + // To prevent overflow after scaling, downscale the input if needed: + double extra_scale = 1.; + int32_t limit = int32_t(std::min(std::numeric_limits::max() / (2. * scale_x), std::numeric_limits::max() / (2. * scale_y))); + int32_t max_coord = 0; + for (const Point& pt : exp.contour) + max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y()))); + if (max_coord + m_contour_width >= limit) + extra_scale = 0.9 * double(limit) / max_coord; + + ExPolygon exp_copy = exp; + if (extra_scale != 1.) + exp_copy.scale(extra_scale); + exp_copy.scale(scale_x, scale_y); + + ExPolygons expolys_exp = offset_ex(exp_copy, scale_(m_contour_width)); + expolys_exp = diff_ex(expolys_exp, ExPolygons({exp_copy})); + + for (ExPolygon& e : expolys_exp) { + e.scale(1./scale_x, 1./scale_y); + if (extra_scale != 1.) + e.scale(1./extra_scale); + } + + triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.); GLModel::Geometry init_data = GLModel::Geometry(); init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; From e676d40df5c90ea2773f12b6aed312c628612887 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 21 Sep 2022 13:40:24 +0200 Subject: [PATCH 72/93] Cut WIP: Beatifications for input window dialog + Fixed rendering of the connectors, when cut plane is rotated for 270 deg by Y axis --- src/imgui/imconfig.h | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 89 ++++++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 1 + 4 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index f9fdf575b..678c8fe20 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -172,6 +172,7 @@ namespace ImGui const wchar_t WarningMarkerSmall = 0x2618; const wchar_t ExpandBtn = 0x2619; const wchar_t CollapseBtn = 0x2620; + const wchar_t InfoMarkerSmall = 0x2621; // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 2adebbbec..00a11ff37 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -327,7 +327,7 @@ void GLGizmoCut3D::rotate_vec3d_around_plane_center(Vec3d&vec) vec = Transformation( assemble_transform(m_plane_center) * m_rotation_m * assemble_transform(-m_plane_center)).get_matrix() * vec; } -void GLGizmoCut3D::put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset) +void GLGizmoCut3D::put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset) { ModelObject* mo = m_c->selection_info()->model_object(); if (CutConnectors& connectors = mo->cut_connectors; !connectors.empty()) { @@ -369,7 +369,7 @@ void GLGizmoCut3D::update_clipper() m_c->object_clipper()->set_range_and_pos(normal, offset, dist); - put_connetors_on_cut_plane(normal, offset); + put_connectors_on_cut_plane(normal, offset); if (m_raycasters.empty()) on_register_raycasters_for_picking(); @@ -487,7 +487,6 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, double& void GLGizmoCut3D::render_move_center_input(int axis) { - ImGui::AlignTextToFramePadding(); m_imgui->text(m_axis_names[axis]+":"); ImGui::SameLine(); ImGui::PushItemWidth(0.3f*m_control_width); @@ -637,7 +636,7 @@ void GLGizmoCut3D::render_cut_center_grabber() line_shader->set_uniform("emission_factor", 0.1f); line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - const Transform3d trafo = view_matrix * assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(1.0, 1.0, m_grabber_connection_len)); + const Transform3d trafo = view_matrix * scale_transform(Vec3d(1.0, 1.0, m_grabber_connection_len)); line_shader->set_uniform("view_model_matrix", trafo); line_shader->set_uniform("normal_matrix", (Matrix3d)trafo.matrix().block(0, 0, 3, 3).inverse().transpose()); line_shader->set_uniform("width", 0.2f); @@ -651,7 +650,7 @@ void GLGizmoCut3D::render_cut_center_grabber() auto render_rotation_snapping = [shader, camera, this](Axis axis, const ColorRGBA& color) { - Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(m_plane_center) * m_start_dragging_m; + Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_start_dragging_m; if (axis == X) view_model_matrix = view_model_matrix * rotation_transform( 0.5 * PI * Vec3d::UnitY()) * rotation_transform(-PI * Vec3d::UnitZ()); @@ -690,7 +689,7 @@ void GLGizmoCut3D::render_cut_center_grabber() if ((!m_dragging && m_hover_id < 0)) render_grabber_connection(color); - render(m_sphere.model, color, view_matrix * assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + render(m_sphere.model, color, view_matrix * scale_transform(size)); if (!m_dragging && m_hover_id < 0 || m_hover_id == Z) { @@ -914,7 +913,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() return; const Vec3d& instance_offset = mo->instances[inst_id]->get_offset(); - const float sla_shift = m_c->selection_info()->get_sla_shift(); + const double sla_shift = double(m_c->selection_info()->get_sla_shift()); const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; @@ -922,7 +921,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; - double height = connector.height; + float height = connector.height; // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; if (connector.attribs.type == CutConnectorType::Dowel && @@ -932,11 +931,8 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() } pos[Z] += sla_shift; - m_raycasters[i]->set_transform(assemble_transform( - pos, - Transformation(m_rotation_m).get_rotation(), - Vec3d(connector.radius, connector.radius, height) - )); + const Transform3d scale_trafo = scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); + m_raycasters[i]->set_transform(translation_transform(pos) * m_rotation_m * scale_trafo); } } else { @@ -959,7 +955,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() m_raycasters[3]->set_transform(trafo * assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), scale)); offset = 1.25 * size * Vec3d::UnitZ(); - m_raycasters[4]->set_transform(trafo * assemble_transform(Vec3d::Zero(), Vec3d::Zero(), size * Vec3d::Ones())); + m_raycasters[4]->set_transform(trafo * scale_transform(size)); m_raycasters[5]->set_transform(trafo * assemble_transform(-offset, PI * Vec3d::UnitX(), scale)); m_raycasters[6]->set_transform(trafo * assemble_transform(offset, Vec3d::Zero(), scale)); } @@ -1400,7 +1396,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) m_imgui->disabled_begin(connectors.empty()); ImGui::SameLine(m_label_width); - if (m_imgui->button(" " + _L("Reset") + " ##connectors")) + if (m_imgui->button(wxString(ImGui::RevertButton) + _L("Reset") + " ##connectors")) reset_connectors(); m_imgui->disabled_end(); @@ -1428,6 +1424,8 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) connectors[idx].radius_tolerance = 0.01f * float(m_connector_size_tolerance); }); + ImGui::Separator(); + if (m_imgui->button(_L("Confirm connectors"))) { m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); unselect_all_connectors(); @@ -1475,34 +1473,43 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) if (m_mode == size_t(CutMode::cutPlanar)) { ImGui::AlignTextToFramePadding(); + m_imgui->text(wxString(ImGui::InfoMarkerSmall)); + ImGui::SameLine(); m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Hold SHIFT key and connect some two points of an object to cut by line")); ImGui::Separator(); render_build_size(); + ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Cut position: ")); + ImGui::SameLine(m_label_width); render_move_center_input(Z); - - if (m_imgui->button("Reset cutting plane")) + ImGui::SameLine(); + if (m_imgui->button(wxString(ImGui::RevertButton) + _L("Reset cutting plane"))) reset_cut_plane(); + if (wxGetApp().plater()->printer_technology() == ptFFF) { + m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); + if (m_imgui->button(_L("Add/Edit connectors"))) + set_connectors_editing(true); + m_imgui->disabled_end(); + } + ImGui::Separator(); - auto render_part_action_line = [this, connectors](const wxString& info_label, const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { + auto render_part_action_line = [this, connectors](const wxString& label, const wxString& suffix, bool& keep_part, bool& place_on_cut_part, bool& rotate_part) { bool keep = true; - - m_imgui->text(info_label); - ImGui::SameLine(m_label_width); + ImGui::AlignTextToFramePadding(); m_imgui->text(label); - ImGui::SameLine(2 * m_label_width); + ImGui::SameLine(m_label_width); m_imgui->disabled_begin(!connectors.empty()); m_imgui->checkbox(_L("Keep") + suffix, connectors.empty() ? keep_part : keep); m_imgui->disabled_end(); - ImGui::SameLine(3 * m_label_width); + ImGui::SameLine(2 * m_label_width); m_imgui->disabled_begin(!keep_part); if (m_imgui->checkbox(_L("Place on cut") + suffix, place_on_cut_part)) @@ -1513,23 +1520,16 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) m_imgui->disabled_end(); }; - ImGui::AlignTextToFramePadding(); - render_part_action_line(_L("After cut") + ": ", _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); - render_part_action_line("", _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); - } - - if (wxGetApp().plater()->printer_technology() == ptFFF) { - m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); - if (m_imgui->button(_L("Add/Edit connectors"))) - set_connectors_editing(true); - m_imgui->disabled_end(); + m_imgui->text(_L("After cut") + ": "); + render_part_action_line( _L("Upper part"), "##upper", m_keep_upper, m_place_on_cut_upper, m_rotate_upper); + render_part_action_line( _L("Lower part"), "##lower", m_keep_lower, m_place_on_cut_lower, m_rotate_lower); } ImGui::Separator(); m_imgui->disabled_begin(!can_perform_cut()); if(m_imgui->button(_L("Perform cut"))) - perform_cut(m_parent.get_selection());; + perform_cut(m_parent.get_selection()); m_imgui->disabled_end(); } @@ -1557,8 +1557,8 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) void GLGizmoCut3D::render_input_window_warning() const { - if (wxGetApp().plater()->printer_technology() == ptFFF) - m_imgui->text(m_has_invalid_connector ? wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.") : wxString()); + if (wxGetApp().plater()->printer_technology() == ptFFF && m_has_invalid_connector) + m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.")); if (!m_keep_upper && !m_keep_lower) m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Invalid state. \nNo one part is selected for keep after cut")); } @@ -1655,7 +1655,7 @@ void GLGizmoCut3D::render_connectors() for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; - double height = double(connector.height); + float height = connector.height; // recalculate connector position to world position Vec3d pos = connector.pos + instance_offset; if (connector.attribs.type == CutConnectorType::Dowel && @@ -1694,11 +1694,8 @@ void GLGizmoCut3D::render_connectors() m_shapes[connector.attribs].model.set_color(render_color); - const Transform3d view_model_matrix = camera.get_view_matrix() * assemble_transform( - pos, - Transformation(m_rotation_m).get_rotation(), - Vec3d(connector.radius, connector.radius, height) - ); + const Transform3d scale_trafo = scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * scale_trafo; shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); @@ -1741,6 +1738,12 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d rotation = Transformation(m_rotation_m).get_rotation(); + // FIXME experiments with transformations + const Vec3d recover_rot = Transformation(rotation_transform(rotation)).get_rotation(); + if (recover_rot != rotation) + printf("\n ERROR! wrong recovered rotation"); + /////////////// + const bool has_connectors = !mo->cut_connectors.empty(); { Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); @@ -1761,7 +1764,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); rotate_vec3d_around_plane_center(shifted_center); Vec3d norm = (shifted_center - m_plane_center).normalized(); - connector.pos += norm * (0.5 * connector.height); + connector.pos += norm * 0.5 * double(connector.height); } } mo->apply_cut_connectors(_u8L("Connector")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index ac3113a0a..5bc47f49c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -146,7 +146,7 @@ public: void shift_cut_z(double delta); void rotate_vec3d_around_plane_center(Vec3d&vec); - void put_connetors_on_cut_plane(const Vec3d& cp_normal, double cp_offset); + void put_connectors_on_cut_plane(const Vec3d& cp_normal, double cp_offset); void update_clipper(); void update_clipper_on_render(); void set_connectors_editing() { m_connectors_editing = true; } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e9e5137be..22451c60c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -60,6 +60,7 @@ static const std::map font_icons = { {ImGui::CollapseBtn , "collapse_btn" }, {ImGui::RevertButton , "undo" }, {ImGui::WarningMarkerSmall , "notification_warning" }, + {ImGui::InfoMarkerSmall , "notification_info" }, }; static const std::map font_icons_large = { From 0fcb7243b7da92dee35fd728e10a630b37bcbbb5 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 21 Sep 2022 16:29:06 +0200 Subject: [PATCH 73/93] Cut WIP: Upgrade for reset_buttons in inpot_window --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 22 +++++++--------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 00a11ff37..498668896 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -526,29 +526,27 @@ void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode) m_connector_mode = mode; } -bool GLGizmoCut3D::render_revert_button(const std::string& label_id) +bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::string& tooltip) const { const ImGuiStyle& style = ImGui::GetStyle(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y }); - ImGui::SameLine(m_label_width); ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f }); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f }); ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.4f, 0.4f, 0.4f, 1.0f }); - std::string label; - label += ImGui::RevertButton; - bool revert = ImGui::Button((label + "##" + label_id).c_str()); + std::string btn_label; + btn_label += ImGui::RevertButton; + const bool revert = ImGui::Button((btn_label +"##" + label_id).c_str()); ImGui::PopStyleColor(3); if (ImGui::IsItemHovered()) - m_imgui->tooltip(into_u8(_L("Revert")).c_str(), ImGui::GetFontSize() * 20.0f); + m_imgui->tooltip(tooltip.c_str(), ImGui::GetFontSize() * 20.0f); ImGui::PopStyleVar(); - ImGui::SameLine(); return revert; } @@ -1396,7 +1394,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) m_imgui->disabled_begin(connectors.empty()); ImGui::SameLine(m_label_width); - if (m_imgui->button(wxString(ImGui::RevertButton) + _L("Reset") + " ##connectors")) + if (render_reset_button("connectors", _u8L("Remove connectors"))) reset_connectors(); m_imgui->disabled_end(); @@ -1486,7 +1484,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::SameLine(m_label_width); render_move_center_input(Z); ImGui::SameLine(); - if (m_imgui->button(wxString(ImGui::RevertButton) + _L("Reset cutting plane"))) + if (render_reset_button("cut_plane", _u8L("Reset cutting plane"))) reset_cut_plane(); if (wxGetApp().plater()->printer_technology() == ptFFF) { @@ -1738,12 +1736,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d rotation = Transformation(m_rotation_m).get_rotation(); - // FIXME experiments with transformations - const Vec3d recover_rot = Transformation(rotation_transform(rotation)).get_rotation(); - if (recover_rot != rotation) - printf("\n ERROR! wrong recovered rotation"); - /////////////// - const bool has_connectors = !mo->cut_connectors.empty(); { Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 5bc47f49c..fc2347c64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -210,7 +210,7 @@ private: bool render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in); void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); - bool render_revert_button(const std::string& label); + bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; bool render_connect_type_radio_button(CutConnectorType type); Transform3d get_volume_transformation(const ModelVolume* volume) const; void render_connectors(); From 66e2c3b30a6208ea373fc6878c13c71f6363c7ad Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 26 Sep 2022 17:23:40 +0200 Subject: [PATCH 74/93] Cut WIP: Send to the cut() whole cut_matrix instead of cut_plane_pos and rotation angles + Fixed units inside input window + NotificationManager: Added info line for loaded object with cut parts + Next Code refactoring --- src/libslic3r/Model.cpp | 501 +++++++++++++------------ src/libslic3r/Model.hpp | 25 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 92 +++-- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/NotificationManager.cpp | 1 + src/slic3r/GUI/Plater.cpp | 4 +- src/slic3r/GUI/Plater.hpp | 2 +- 7 files changed, 325 insertions(+), 302 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 82b5fad57..3d245a7b1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1381,11 +1381,13 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn return connector_mesh; } -void ModelObject::apply_cut_connectors(const std::string& name) +void ModelObject::apply_cut_connectors(const std::string& new_name) { if (cut_connectors.empty()) return; + using namespace Geometry; + size_t connector_id = cut_id.connectors_cnt(); for (const CutConnector& connector : cut_connectors) { TriangleMesh mesh = TriangleMesh(get_connector_mesh(connector.attribs)); @@ -1393,15 +1395,11 @@ void ModelObject::apply_cut_connectors(const std::string& name) ModelVolume* new_volume = add_volume(std::move(mesh), ModelVolumeType::NEGATIVE_VOLUME); // Transform the new modifier to be aligned inside the instance - new_volume->set_transformation(Geometry::assemble_transform( - connector.pos, - connector.rotation, - Vec3d(connector.radius, connector.radius, connector.height), - Vec3d::Ones() - )); + new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m * + scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; - new_volume->name = name + "-" + std::to_string(++connector_id); + new_volume->name = new_name + "-" + std::to_string(++connector_id); } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1426,14 +1424,8 @@ void ModelObject::synchronize_model_after_cut() } } -ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) +void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) { - if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) - return {}; - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - - // initiate/update cut attributes for object if (cut_id.id().invalid()) cut_id.init(); { @@ -1444,25 +1436,241 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const if (cut_obj_cnt > 0) cut_id.increase_check_sum(size_t(cut_obj_cnt)); } +} - auto clone_obj = [this](ModelObject** obj) { - (*obj) = ModelObject::new_clone(*this); - (*obj)->set_model(nullptr); - (*obj)->sla_support_points.clear(); - (*obj)->sla_drain_holes.clear(); - (*obj)->sla_points_status = sla::PointsStatus::NoPoints; - (*obj)->clear_volumes(); - (*obj)->input_file.clear(); - }; +void ModelObject::clone_for_cut(ModelObject** obj) +{ + (*obj) = ModelObject::new_clone(*this); + (*obj)->set_model(nullptr); + (*obj)->sla_support_points.clear(); + (*obj)->sla_drain_holes.clear(); + (*obj)->sla_points_status = sla::PointsStatus::NoPoints; + (*obj)->clear_volumes(); + (*obj)->input_file.clear(); +} + +void ModelVolume::apply_tolerance() +{ + if (!cut_info.is_connector) + return; + + Vec3d sf = get_scaling_factor(); +/* + // correct Z offset in respect to the new size + Vec3d pos = vol->get_offset(); + pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance; + vol->set_offset(pos); +*/ + // make a "hole" wider + sf[X] *= 1. + double(cut_info.radius_tolerance); + sf[Y] *= 1. + double(cut_info.radius_tolerance); + + // make a "hole" dipper + sf[Z] *= 1. + double(cut_info.height_tolerance); + + set_scaling_factor(sf); +} + +void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels, Vec3d& local_dowels_displace) +{ + volume->cut_info.discard(); + + const auto volume_matrix = volume->get_matrix(); + + // ! Don't apply instance transformation for the conntectors. + // This transformation is already there + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { + ModelVolume* vol = upper->add_volume(*volume); + vol->set_transformation(volume_matrix); + vol->apply_tolerance(); + } + if (attributes.has(ModelObjectCutAttribute::KeepLower)) { + ModelVolume* vol = lower->add_volume(*volume); + vol->set_transformation(volume_matrix); + + if (volume->cut_info.connector_type == CutConnectorType::Dowel) + vol->apply_tolerance(); + else + // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug + vol->set_type(ModelVolumeType::MODEL_PART); + } + if (volume->cut_info.connector_type == CutConnectorType::Dowel && + attributes.has(ModelObjectCutAttribute::CreateDowels)) { + ModelObject* dowel{ nullptr }; + // Clone the object to duplicate instances, materials etc. + clone_for_cut(&dowel); + + // add one more solid part same as connector if this connector is a dowel + ModelVolume* vol = dowel->add_volume(*volume); + vol->set_type(ModelVolumeType::MODEL_PART); + + // But discard rotation and Z-offset for this volume + vol->set_rotation(Vec3d::Zero()); + vol->set_offset(Z, 0.0); + + // Compute the displacement (in instance coordinates) to be applied to place the dowels + local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); + + dowels.push_back(dowel); + } +} + +void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) +{ + const auto volume_matrix = volume->get_matrix(); + + // Modifiers are not cut, but we still need to add the instance transformation + // to the modifier volume transformation to preserve their shape properly. + volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); + + // Some logic for the negative volumes/connectors. Add only needed modifiers + auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); + bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) + upper->add_volume(*volume); + if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) + lower->add_volume(*volume); +} + +static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix) +{ + if (mesh.empty()) + return; + + mesh.transform(cut_matrix); + ModelVolume* vol = object->add_volume(mesh); + + vol->name = src_volume->name; + // Don't copy the config's ID. + vol->config.assign_config(src_volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != src_volume->config.id()); + vol->set_material(src_volume->material_id(), *src_volume->material()); +} + +void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace) +{ + const auto volume_matrix = volume->get_matrix(); + + using namespace Geometry; + + const Transformation cut_transformation = Transformation(cut_matrix); + const Transform3d invert_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1 * cut_transformation.get_offset()); + + // Transform the mesh by the combined transformation matrix. + // Flip the triangles in case the composite transformation is left handed. + TriangleMesh mesh(volume->mesh()); + mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true); + + volume->reset_mesh(); + // Reset volume transformation except for offset + const Vec3d offset = volume->get_offset(); + volume->set_transformation(Geometry::Transformation()); + volume->set_offset(offset); + + // Perform cut + + TriangleMesh upper_mesh, lower_mesh; + { + indexed_triangle_set upper_its, lower_its; + cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + upper_mesh = TriangleMesh(upper_its); + if (attributes.has(ModelObjectCutAttribute::KeepLower)) + lower_mesh = TriangleMesh(lower_its); + } + + // Add required cut parts to the objects + + if (attributes.has(ModelObjectCutAttribute::KeepUpper)) + add_cut_volume(upper_mesh, upper, volume, cut_matrix); + + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { + add_cut_volume(lower_mesh, lower, volume, cut_matrix); + + // Compute the displacement (in instance coordinates) to be applied to place the upper parts + // The upper part displacement is set to half of the lower part bounding box + // this is done in hope at least a part of the upper part will always be visible and draggable + local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); + } +} + +static void invalidate_translations(ModelObject* object, const ModelInstance* src_instance) +{ + if (!object->origin_translation.isApprox(Vec3d::Zero()) && src_instance->get_offset().isApprox(Vec3d::Zero())) { + object->center_around_origin(); + object->translate_instances(-object->origin_translation); + object->origin_translation = Vec3d::Zero(); + } + else { + object->invalidate_bounding_box(); + object->center_around_origin(); + } +} + +static void reset_instance_transformation(ModelObject* object, size_t src_instance_idx, const Transform3d& cut_matrix, + bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero()) +{ + using namespace Geometry; + static Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); + + // Reset instance transformation except offset and Z-rotation + + for (size_t i = 0; i < object->instances.size(); ++i) { + auto& obj_instance = object->instances[i]; + const Vec3d offset = obj_instance->get_offset(); + const double rot_z = obj_instance->get_rotation().z(); + + obj_instance->set_transformation(Transformation()); + + const Vec3d displace = local_displace.isApprox(Vec3d::Zero()) ? Vec3d::Zero() : + assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace; + obj_instance->set_offset(offset + displace); + + Vec3d rotation = Vec3d::Zero(); + if (!flip && !place_on_cut) { + if ( i != src_instance_idx) + rotation[Z] = rot_z; + } + else { + Transform3d rotation_matrix = Transform3d::Identity(); + if (flip) + rotation_matrix = rotation_transform(rotate_z180); + + if (place_on_cut) + rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse(); + + if (i != src_instance_idx) + rotation_matrix = rotation_transform(rot_z * Vec3d::UnitZ()) * rotation_matrix; + + rotation = Transformation(rotation_matrix).get_rotation(); + } + + obj_instance->set_rotation(rotation); + } +} + +ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) +{ + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) && !attributes.has(ModelObjectCutAttribute::KeepLower)) + return {}; + + BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; + + // apply cut attributes for object + apply_cut_attributes(attributes); // Clone the object to duplicate instances, materials etc. ModelObject* upper{ nullptr }; if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - clone_obj(&upper); + clone_for_cut(&upper); ModelObject* lower{ nullptr }; if (attributes.has(ModelObjectCutAttribute::KeepLower)) - clone_obj(&lower); + clone_for_cut(&lower); std::vector dowels; @@ -1476,260 +1684,67 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Vec3d& cut_center, const // const auto instance_matrix = instances[instance]->get_matrix(true); const auto instance_matrix = assemble_transform( Vec3d::Zero(), // don't apply offset - instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 1.0)), + instances[instance]->get_rotation(), instances[instance]->get_scaling_factor(), instances[instance]->get_mirror() ); - const auto cut_matrix = rotation_transform(cut_rotation).inverse() * assemble_transform(-cut_center); - const auto invert_cut_matrix = assemble_transform(cut_center, cut_rotation); + const Transformation cut_transformation = Transformation(cut_matrix); + const Transform3d inverse_cut_matrix = cut_transformation.get_rotation_matrix().inverse() * assemble_transform(-1. * cut_transformation.get_offset()); // Displacement (in instance coordinates) to be applied to place the upper parts Vec3d local_displace = Vec3d::Zero(); Vec3d local_dowels_displace = Vec3d::Zero(); - Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); - - auto apply_tolerance = [](ModelVolume * vol) - { - Vec3d sf = vol->get_scaling_factor(); -/* - // correct Z offset in respect to the new size - Vec3d pos = vol->get_offset(); - pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance; - vol->set_offset(pos); -*/ - // make a "hole" wider - sf[X] *= (1 + vol->cut_info.radius_tolerance); - sf[Y] *= (1 + vol->cut_info.radius_tolerance); - // make a "hole" dipper - sf[Z] *= (1 + vol->cut_info.height_tolerance); - vol->set_scaling_factor(sf); - }; - for (ModelVolume* volume : volumes) { - const auto volume_matrix = volume->get_matrix(); - volume->supported_facets.reset(); volume->seam_facets.reset(); volume->mmu_segmentation_facets.reset(); if (!volume->is_model_part()) { - if (volume->cut_info.is_connector) { - volume->cut_info.discard(); - - // ! Don't apply instance transformation for the conntectors. - // This transformation is already there - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - ModelVolume* vol = upper->add_volume(*volume); - vol->set_transformation(volume_matrix); - apply_tolerance(vol); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - ModelVolume* vol = lower->add_volume(*volume); - vol->set_transformation(volume_matrix); - - if (volume->cut_info.connector_type == CutConnectorType::Dowel) - apply_tolerance(vol); - else - // for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug - vol->set_type(ModelVolumeType::MODEL_PART); - } - if (volume->cut_info.connector_type == CutConnectorType::Dowel && - attributes.has(ModelObjectCutAttribute::CreateDowels)) { - ModelObject* dowel{ nullptr }; - // Clone the object to duplicate instances, materials etc. - clone_obj(&dowel); - - // add one more solid part same as connector if this connector is a dowel - ModelVolume* vol = dowel->add_volume(*volume); - vol->set_type(ModelVolumeType::MODEL_PART); - - // But discard rotation and Z-offset for this volume - vol->set_rotation(Vec3d::Zero()); - vol->set_offset(Z, 0.0); - - // Compute the displacement (in instance coordinates) to be applied to place the dowels - local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0)); - - dowels.push_back(dowel); - } - } - else { - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - - // Some logic for the negative volumes/connectors. Add only needed modifiers - auto bb = volume->mesh().transformed_bounding_box(cut_matrix * volume->get_matrix()); - bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0; - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && (bb.min[Z] >= 0 || is_crossed_by_cut)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower) && (bb.max[Z] <= 0 || is_crossed_by_cut)) - lower->add_volume(*volume); - } - } - else if (!volume->mesh().empty()) { - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(cut_matrix * instance_matrix* volume_matrix, true); - - volume->reset_mesh(); - // Reset volume transformation except for offset - const Vec3d offset = volume->get_offset(); - volume->set_transformation(Geometry::Transformation()); - volume->set_offset(offset); - - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - { - indexed_triangle_set upper_its, lower_its; - cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper_mesh = TriangleMesh(upper_its); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower_mesh = TriangleMesh(lower_its); - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper_mesh.empty()) { - upper_mesh.transform(invert_cut_matrix); - - ModelVolume* vol = upper->add_volume(upper_mesh); - vol->name = volume->name; - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower_mesh.empty()) { - lower_mesh.transform(invert_cut_matrix); - - ModelVolume* vol = lower->add_volume(lower_mesh); - vol->name = volume->name; - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - - // Compute the displacement (in instance coordinates) to be applied to place the upper parts - // The upper part displacement is set to half of the lower part bounding box - // this is done in hope at least a part of the upper part will always be visible and draggable - local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); - } + if (volume->cut_info.is_connector) + process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace); + else + process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); } + else if (!volume->mesh().empty()) + process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace); } + // Post-process cut parts + ModelObjectPtrs res; if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - upper->center_around_origin(); - upper->translate_instances(-upper->origin_translation); - upper->origin_translation = Vec3d::Zero(); - } - else { - upper->invalidate_bounding_box(); - upper->center_around_origin(); - } - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = upper->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - const double rot_z = obj_instance->get_rotation().z(); - const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), obj_instance->get_rotation()) * local_displace; + invalidate_translations(upper, instances[instance]); - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset + displace); - - Vec3d rotation = Vec3d::Zero(); - if (attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper)) { - Transform3d trafo = rotation_transform(cut_rotation).inverse(); - if (i != instance) - trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; - rotation = Transformation(trafo).get_rotation(); - } - else if (attributes.has(ModelObjectCutAttribute::FlipUpper)) { - rotation = rotate_z180; - if (i != instance) - rotation[Z] = rot_z; - } - else if (i != instance) - rotation[Z] = rot_z/* * Vec3d::UnitZ()*/; - obj_instance->set_rotation(rotation); - } + reset_instance_transformation(upper, instance, cut_matrix, + attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), + attributes.has(ModelObjectCutAttribute::FlipUpper), + local_displace); res.push_back(upper); } + if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - lower->center_around_origin(); - lower->translate_instances(-lower->origin_translation); - lower->origin_translation = Vec3d::Zero(); - } - else { - lower->invalidate_bounding_box(); - lower->center_around_origin(); - } - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = lower->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - const double rot_z = obj_instance->get_rotation().z(); - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset); + invalidate_translations(lower, instances[instance]); - Vec3d rotation = Vec3d::Zero(); - if (attributes.has(ModelObjectCutAttribute::PlaceOnCutLower)) { - Transform3d trafo = rotation_transform(rotate_z180) * rotation_transform(cut_rotation).inverse(); - if (i != instance) - trafo = rotation_transform(rot_z * Vec3d::UnitZ()) * trafo; - rotation = Transformation(trafo).get_rotation(); - } - else if (attributes.has(ModelObjectCutAttribute::FlipLower)) { - rotation = rotate_z180; - if (i != instance) - rotation[Z] = rot_z; - } - else if (i != instance) - rotation[Z] = rot_z; - obj_instance->set_rotation(rotation); - } + reset_instance_transformation(lower, instance, cut_matrix, + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), + attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); res.push_back(lower); } if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels.size() > 0) { for (auto dowel : dowels) { - if (!dowel->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - dowel->center_around_origin(); - dowel->translate_instances(-dowel->origin_translation); - dowel->origin_translation = Vec3d::Zero(); - } - else { - dowel->invalidate_bounding_box(); - dowel->center_around_origin(); - } + invalidate_translations(dowel, instances[instance]); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto& obj_instance = dowel->instances[i]; - const Vec3d offset = obj_instance->get_offset(); - Vec3d rotation = Vec3d::Zero(); - if (i != instance) - rotation[Z] = obj_instance->get_rotation().z(); - - const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), rotation) * local_dowels_displace; - - obj_instance->set_transformation(Geometry::Transformation()); - obj_instance->set_offset(offset + displace); - obj_instance->set_rotation(rotation); - } + reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); res.push_back(dowel); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 21b052337..6981946fb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -271,7 +271,7 @@ struct CutConnectorAttributes struct CutConnector { Vec3d pos; - Vec3d rotation; + Transform3d rotation_m; float radius; float height; float radius_tolerance;// [0.f : 1.f] @@ -279,22 +279,22 @@ struct CutConnector CutConnectorAttributes attribs; CutConnector() - : pos(Vec3d::Zero()), rotation(Vec3d::UnitZ()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) + : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) {} - CutConnector(Vec3d p, Vec3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) - : pos(p), rotation(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) + : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} bool operator==(const CutConnector& other) const; bool operator!=(const CutConnector& other) const { return !(other == (*this)); } template inline void serialize(Archive& ar) { - ar(pos, rotation, radius, height, radius_tolerance, height_tolerance, attribs); + ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); } }; @@ -445,8 +445,16 @@ public: // invalidate cut state for this and related objects from the whole model void invalidate_cut(); void synchronize_model_after_cut(); - ModelObjectPtrs cut(size_t instance, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); - void split(ModelObjectPtrs* new_objects); + void apply_cut_attributes(ModelObjectCutAttributes attributes); + void clone_for_cut(ModelObject **obj); + void process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, + std::vector& dowels, Vec3d& local_dowels_displace); + void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower); + void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, + ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace); + ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes); + void split(ModelObjectPtrs*new_objects); void merge(); // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. @@ -760,6 +768,7 @@ public: bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } t_model_material_id material_id() const { return m_material_id; } + void apply_tolerance(); void set_material_id(t_model_material_id material_id); ModelMaterial* material() const; void set_material(t_model_material_id material_id, const ModelMaterial &material); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 498668896..1260ee3bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -800,10 +800,9 @@ void GLGizmoCut3D::on_load(cereal::BinaryInputArchive& ar) { ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, - m_ar_plane_center, m_ar_rotations); + m_ar_plane_center, m_rotation_m); set_center_pos(m_ar_plane_center, true); - m_rotation_m = rotation_transform(m_ar_rotations); force_update_clipper_on_render = true; @@ -814,7 +813,7 @@ void GLGizmoCut3D::on_save(cereal::BinaryOutputArchive& ar) const { ar( m_keep_upper, m_keep_lower, m_rotate_lower, m_rotate_upper, m_hide_cut_plane, m_mode, m_connectors_editing,//m_selected, // m_connector_depth_ratio, m_connector_size, m_connector_mode, m_connector_type, m_connector_style, m_connector_shape_id, - m_ar_plane_center, m_ar_rotations); + m_ar_plane_center, m_start_dragging_m); } std::string GLGizmoCut3D::on_get_name() const @@ -829,7 +828,7 @@ void GLGizmoCut3D::on_set_state() // initiate archived values m_ar_plane_center = m_plane_center; - m_ar_rotations = Transformation(m_rotation_m).get_rotation(); + m_start_dragging_m = m_rotation_m; m_parent.request_extra_frame(); } @@ -1119,8 +1118,6 @@ void GLGizmoCut3D::on_stop_dragging() m_angle_arc.reset(); m_angle = 0.0; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Rotate cut plane"), UndoRedo::SnapshotType::GizmoAction); - m_ar_rotations = Transformation(m_rotation_m).get_rotation(); - m_start_dragging_m = m_rotation_m; } else if (m_hover_id == Z) { @@ -1436,7 +1433,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) void GLGizmoCut3D::render_build_size() { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; - std::string unit_str = m_imperial_units ? _u8L("inch") : _u8L("mm"); + wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); const BoundingBoxf3 tbb = transformed_bounding_box(); Vec3d tbb_sz = tbb.size(); @@ -1710,6 +1707,31 @@ bool GLGizmoCut3D::can_perform_cut() const return tbb.contains(m_plane_center); } +void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object) +{ + if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { + m_selected.clear(); + + for (CutConnector&connector : mo->cut_connectors) { + connector.rotation_m = m_rotation_m; + + if (connector.attribs.type == CutConnectorType::Dowel) { + if (connector.attribs.style == CutConnectorStyle::Prizm) + connector.height *= 2; + create_dowels_as_separate_object = true; + } + else { + // culculate shift of the connector center regarding to the position on the cut plane + Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); + rotate_vec3d_around_plane_center(shifted_center); + Vec3d norm = (shifted_center - m_plane_center).normalized(); + connector.pos += norm * 0.5 * double(connector.height); + } + } + mo->apply_cut_connectors(_u8L("Connector")); + } +} + void GLGizmoCut3D::perform_cut(const Selection& selection) { const int instance_idx = selection.get_instance_idx(); @@ -1717,53 +1739,29 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection"); - // m_cut_z is the distance from the bed. Subtract possible SLA elevation. - const GLVolume* first_glvolume = selection.get_first_volume(); - const double object_cut_z = m_plane_center.z() - first_glvolume->get_sla_shift_z(); - - const Vec3d& instance_offset = wxGetApp().plater()->model().objects[object_idx]->instances[instance_idx]->get_offset(); - - Vec3d cut_center_offset = m_plane_center - instance_offset; - cut_center_offset[Z] -= first_glvolume->get_sla_shift_z(); - Plater* plater = wxGetApp().plater(); + ModelObject* mo = plater->model().objects[object_idx]; + if (!mo) + return; + + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. + const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); + const double object_cut_z = m_plane_center.z() - sla_shift_z; + + const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); + Vec3d cut_center_offset = m_plane_center - instance_offset; + cut_center_offset[Z] -= sla_shift_z; - bool create_dowels_as_separate_object = false; if (0.0 < object_cut_z && can_perform_cut()) { - ModelObject* mo = plater->model().objects[object_idx]; - if(!mo) - return; - - Vec3d rotation = Transformation(m_rotation_m).get_rotation(); - + bool create_dowels_as_separate_object = false; const bool has_connectors = !mo->cut_connectors.empty(); { - Plater::TakeSnapshot snapshot(plater, _L("Cut by Plane")); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); // update connectors pos as offset of its center before cut performing - if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { - m_selected.clear(); - - for (CutConnector& connector : mo->cut_connectors) { - connector.rotation = rotation; - - if (connector.attribs.type == CutConnectorType::Dowel) { - if (connector.attribs.style == CutConnectorStyle::Prizm) - connector.height *= 2; - create_dowels_as_separate_object = true; - } - else { - // culculate shift of the connector center regarding to the position on the cut plane - Vec3d shifted_center = m_plane_center + Vec3d::UnitZ(); - rotate_vec3d_around_plane_center(shifted_center); - Vec3d norm = (shifted_center - m_plane_center).normalized(); - connector.pos += norm * 0.5 * double(connector.height); - } - } - mo->apply_cut_connectors(_u8L("Connector")); - } + apply_connectors_in_model(mo, has_connectors, create_dowels_as_separate_object); } - plater->cut(object_idx, instance_idx, cut_center_offset, rotation, + plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | only_if(has_connectors ? true : m_keep_lower, ModelObjectCutAttribute::KeepLower) | only_if(m_place_on_cut_upper, ModelObjectCutAttribute::PlaceOnCutUpper) | @@ -1925,7 +1923,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - connectors.emplace_back(hit, Transformation(m_rotation_m).get_rotation(), + connectors.emplace_back(hit, m_rotation_m, float(m_connector_size) * 0.5f, float(m_connector_depth_ratio), float(m_connector_size_tolerance) * 0.01f, float(m_connector_depth_ratio_tolerance) * 0.01f, CutConnectorAttributes( CutConnectorType(m_connector_type), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index fc2347c64..2df337b04 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -25,7 +25,6 @@ class GLGizmoCut3D : public GLGizmoBase // archived values Vec3d m_ar_plane_center { Vec3d::Zero() }; - Vec3d m_ar_rotations { Vec3d::Zero() }; Vec3d m_plane_center{ Vec3d::Zero() }; // data to check position of the cut palne center on gizmo activation @@ -216,6 +215,7 @@ private: void render_connectors(); bool can_perform_cut() const; + void apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object); bool cut_line_processing() const; void discard_cut_line_processing(); diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 2e74270da..16433bc79 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1228,6 +1228,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break; case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break; + case InfoItemType::Cut: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break; } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7f351e0b9..4bf441aa1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5967,7 +5967,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCut selection.add_object((unsigned int)(last_id - i), i == 0); } -void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes) +void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) { wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); auto* object = p->model.objects[obj_idx]; @@ -5977,7 +5977,7 @@ void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Vec3d& this->suppress_snapshots(); wxBusyCursor wait; - const auto new_objects = object->cut(instance_idx, cut_center, cut_rotation, attributes); + const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); model().delete_object(obj_idx); sidebar().obj_list()->delete_object_from_list(obj_idx); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7e35e935f..9ee1af272 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -256,7 +256,7 @@ public: void toggle_layers_editing(bool enable); void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes); - void cut(size_t obj_idx, size_t instance_idx, const Vec3d& cut_center, const Vec3d& cut_rotation, ModelObjectCutAttributes attributes); + void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes); void export_gcode(bool prefer_removable); void export_stl_obj(bool extended = false, bool selection_only = false); From e689be65dbb477e4bb97066df03e943bef357a96 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 27 Sep 2022 09:10:09 +0200 Subject: [PATCH 75/93] Code cleaning --- src/PrusaSlicer.cpp | 4 +- src/libslic3r/Model.cpp | 189 +++----------------------------------- src/libslic3r/Model.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 26 +----- src/slic3r/GUI/Plater.hpp | 1 - 5 files changed, 19 insertions(+), 203 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index ecb157218..500028525 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -426,7 +426,9 @@ int CLI::run(int argc, char **argv) o->cut(Z, m_config.opt_float("cut"), &out); } #else - model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower); +// model.objects.front()->cut(0, m_config.opt_float("cut"), ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::FlipLower); + model.objects.front()->cut(0, Geometry::assemble_transform(m_config.opt_float("cut")* Vec3d::UnitZ()), + ModelObjectCutAttribute::KeepLower | ModelObjectCutAttribute::KeepUpper | ModelObjectCutAttribute::PlaceOnCutUpper); #endif model.delete_object(size_t(0)); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3d245a7b1..d4c81299f 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1191,166 +1191,6 @@ size_t ModelObject::parts_count() const return num; } -ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes) -{ - if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) - return {}; - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start"; - - // Clone the object to duplicate instances, materials etc. - ModelObject* upper = attributes.has(ModelObjectCutAttribute::KeepUpper) ? ModelObject::new_clone(*this) : nullptr; - ModelObject* lower = attributes.has(ModelObjectCutAttribute::KeepLower) ? ModelObject::new_clone(*this) : nullptr; - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) { - upper->set_model(nullptr); - upper->sla_support_points.clear(); - upper->sla_drain_holes.clear(); - upper->sla_points_status = sla::PointsStatus::NoPoints; - upper->clear_volumes(); - upper->input_file.clear(); - } - - if (attributes.has(ModelObjectCutAttribute::KeepLower)) { - lower->set_model(nullptr); - lower->sla_support_points.clear(); - lower->sla_drain_holes.clear(); - lower->sla_points_status = sla::PointsStatus::NoPoints; - lower->clear_volumes(); - lower->input_file.clear(); - } - - // Because transformations are going to be applied to meshes directly, - // we reset transformation of all instances and volumes, - // except for translation and Z-rotation on instances, which are preserved - // in the transformation matrix and not applied to the mesh transform. - - // const auto instance_matrix = instances[instance]->get_matrix(true); - const auto instance_matrix = Geometry::assemble_transform( - Vec3d::Zero(), // don't apply offset - instances[instance]->get_rotation().cwiseProduct(Vec3d(1.0, 1.0, 0.0)), // don't apply Z-rotation - instances[instance]->get_scaling_factor(), - instances[instance]->get_mirror() - ); - - z -= instances[instance]->get_offset().z(); - - // Displacement (in instance coordinates) to be applied to place the upper parts - Vec3d local_displace = Vec3d::Zero(); - - for (ModelVolume *volume : volumes) { - const auto volume_matrix = volume->get_matrix(); - - volume->supported_facets.reset(); - volume->seam_facets.reset(); - volume->mmu_segmentation_facets.reset(); - - if (! volume->is_model_part()) { - // Modifiers are not cut, but we still need to add the instance transformation - // to the modifier volume transformation to preserve their shape properly. - - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); - - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper->add_volume(*volume); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower->add_volume(*volume); - } - else if (! volume->mesh().empty()) { - // Transform the mesh by the combined transformation matrix. - // Flip the triangles in case the composite transformation is left handed. - TriangleMesh mesh(volume->mesh()); - mesh.transform(instance_matrix * volume_matrix, true); - volume->reset_mesh(); - // Reset volume transformation except for offset - const Vec3d offset = volume->get_offset(); - volume->set_transformation(Geometry::Transformation()); - volume->set_offset(offset); - - // Perform cut - TriangleMesh upper_mesh, lower_mesh; - { - indexed_triangle_set upper_its, lower_its; - cut_mesh(mesh.its, float(z), &upper_its, &lower_its); - if (attributes.has(ModelObjectCutAttribute::KeepUpper)) - upper_mesh = TriangleMesh(upper_its); - if (attributes.has(ModelObjectCutAttribute::KeepLower)) - lower_mesh = TriangleMesh(lower_its); - } - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && ! upper_mesh.empty()) { - ModelVolume* vol = upper->add_volume(upper_mesh); - vol->name = volume->name; - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && ! lower_mesh.empty()) { - ModelVolume* vol = lower->add_volume(lower_mesh); - vol->name = volume->name; - // Don't copy the config's ID. - vol->config.assign_config(volume->config); - assert(vol->config.id().valid()); - assert(vol->config.id() != volume->config.id()); - vol->set_material(volume->material_id(), *volume->material()); - - // Compute the displacement (in instance coordinates) to be applied to place the upper parts - // The upper part displacement is set to half of the lower part bounding box - // this is done in hope at least a part of the upper part will always be visible and draggable - local_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-0.5, -0.5, 0.0)); - } - } - } - - ModelObjectPtrs res; - - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - if (!upper->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - upper->center_around_origin(); - upper->translate_instances(-upper->origin_translation); - upper->origin_translation = Vec3d::Zero(); - } - - // Reset instance transformation except offset and Z-rotation - for (size_t i = 0; i < instances.size(); ++i) { - auto &instance = upper->instances[i]; - const Vec3d offset = instance->get_offset(); - const double rot_z = instance->get_rotation().z(); - const Vec3d displace = Geometry::assemble_transform(Vec3d::Zero(), instance->get_rotation()) * local_displace; - - instance->set_transformation(Geometry::Transformation()); - instance->set_offset(offset + displace); - instance->set_rotation(Vec3d(0.0, 0.0, rot_z)); - } - - res.push_back(upper); - } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - if (!lower->origin_translation.isApprox(Vec3d::Zero()) && instances[instance]->get_offset().isApprox(Vec3d::Zero())) { - lower->center_around_origin(); - lower->translate_instances(-lower->origin_translation); - lower->origin_translation = Vec3d::Zero(); - } - - // Reset instance transformation except offset and Z-rotation - for (auto *instance : lower->instances) { - const Vec3d offset = instance->get_offset(); - const double rot_z = instance->get_rotation().z(); - instance->set_transformation(Geometry::Transformation()); - instance->set_offset(offset); - instance->set_rotation(Vec3d(attributes.has(ModelObjectCutAttribute::FlipLower) ? Geometry::deg2rad(180.0) : 0.0, 0.0, rot_z)); - } - - res.push_back(lower); - } - - BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end"; - - return res; -} - indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes) { indexed_triangle_set connector_mesh; @@ -1449,6 +1289,13 @@ void ModelObject::clone_for_cut(ModelObject** obj) (*obj)->input_file.clear(); } +void ModelVolume::reset_extra_facets() +{ + this->supported_facets.reset(); + this->seam_facets.reset(); + this->mmu_segmentation_facets.reset(); +} + void ModelVolume::apply_tolerance() { if (!cut_info.is_connector) @@ -1615,7 +1462,6 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan bool place_on_cut = false, bool flip = false, Vec3d local_displace = Vec3d::Zero()) { using namespace Geometry; - static Vec3d rotate_z180 = deg2rad(180.0) * Vec3d::UnitX(); // Reset instance transformation except offset and Z-rotation @@ -1638,7 +1484,7 @@ static void reset_instance_transformation(ModelObject* object, size_t src_instan else { Transform3d rotation_matrix = Transform3d::Identity(); if (flip) - rotation_matrix = rotation_transform(rotate_z180); + rotation_matrix = rotation_transform(PI * Vec3d::UnitX()); if (place_on_cut) rotation_matrix = rotation_matrix * Transformation(cut_matrix).get_rotation_matrix().inverse(); @@ -1697,9 +1543,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, Vec3d local_dowels_displace = Vec3d::Zero(); for (ModelVolume* volume : volumes) { - volume->supported_facets.reset(); - volume->seam_facets.reset(); - volume->mmu_segmentation_facets.reset(); + volume->reset_extra_facets(); if (!volume->is_model_part()) { if (volume->cut_info.is_connector) @@ -1715,38 +1559,33 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, ModelObjectPtrs res; - if (attributes.has(ModelObjectCutAttribute::KeepUpper) && upper->volumes.size() > 0) { - + if (attributes.has(ModelObjectCutAttribute::KeepUpper) && !upper->volumes.empty()) { invalidate_translations(upper, instances[instance]); reset_instance_transformation(upper, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutUpper), attributes.has(ModelObjectCutAttribute::FlipUpper), local_displace); - res.push_back(upper); } - if (attributes.has(ModelObjectCutAttribute::KeepLower) && lower->volumes.size() > 0) { - + if (attributes.has(ModelObjectCutAttribute::KeepLower) && !lower->volumes.empty()) { invalidate_translations(lower, instances[instance]); reset_instance_transformation(lower, instance, cut_matrix, attributes.has(ModelObjectCutAttribute::PlaceOnCutLower), attributes.has(ModelObjectCutAttribute::PlaceOnCutLower) ? true : attributes.has(ModelObjectCutAttribute::FlipLower)); - res.push_back(lower); } - if (attributes.has(ModelObjectCutAttribute::CreateDowels) && dowels.size() > 0) { + if (attributes.has(ModelObjectCutAttribute::CreateDowels) && !dowels.empty()) { for (auto dowel : dowels) { invalidate_translations(dowel, instances[instance]); - dowel->name += "-Dowel-" + dowel->volumes[0]->name; - reset_instance_transformation(dowel, instance, Transform3d::Identity(), false, false, local_dowels_displace); - local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + local_dowels_displace += dowel->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(-1.5, -1.5, 0.0)); + dowel->name += "-Dowel-" + dowel->volumes[0]->name; res.push_back(dowel); } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 6981946fb..16142ec05 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -439,7 +439,6 @@ public: size_t materials_count() const; size_t facets_count() const; size_t parts_count() const; - ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes); static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(const std::string& name); // invalidate cut state for this and related objects from the whole model @@ -768,6 +767,7 @@ public: bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; } bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; } t_model_material_id material_id() const { return m_material_id; } + void reset_extra_facets(); void apply_tolerance(); void set_material_id(t_model_material_id material_id); ModelMaterial* material() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4bf441aa1..d09de9cf3 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5943,31 +5943,7 @@ void Plater::toggle_layers_editing(bool enable) canvas3D()->force_main_toolbar_left_action(canvas3D()->get_main_toolbar_item_id("layersediting")); } -void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes) -{ - 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"); - - if (! attributes.has(ModelObjectCutAttribute::KeepUpper) && ! attributes.has(ModelObjectCutAttribute::KeepLower)) - return; - - Plater::TakeSnapshot snapshot(this, _L("Cut by Plane")); - - wxBusyCursor wait; - const auto new_objects = object->cut(instance_idx, z, attributes); - - remove(obj_idx); - p->load_model_objects(new_objects); - - Selection& selection = p->get_selection(); - size_t last_id = p->model.objects.size() - 1; - for (size_t i = 0; i < new_objects.size(); ++i) - selection.add_object((unsigned int)(last_id - i), i == 0); -} - -void Slic3r::GUI::Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) +void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes) { wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds"); auto* object = p->model.objects[obj_idx]; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9ee1af272..1d7576266 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -255,7 +255,6 @@ public: void convert_unit(ConversionType conv_type); void toggle_layers_editing(bool enable); - void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes); void cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_matrix, ModelObjectCutAttributes attributes); void export_gcode(bool prefer_removable); From 0201a5055aaa1a52e95a21c852b71200f5fc9145 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 27 Sep 2022 13:54:44 +0200 Subject: [PATCH 76/93] =?UTF-8?q?Cut=20WIP:=20*=20Suppress=20to=20split=20?= =?UTF-8?q?cut=20objects=20*=20ObjectList:=20=20=20*=20Use=20another=20ico?= =?UTF-8?q?ns=20to=20mark=20the=20cut=20objects=20and=20connectors=C2=A0?= =?UTF-8?q?=20=20=20*=20For=20the=20cut=20object=20show=20parts,=20which?= =?UTF-8?q?=20are=20not=20connectors=20*=20Set=20different=20colors=20for?= =?UTF-8?q?=20the=20Plugs=20and=20Dowels=20*=20CutGizmo:=20=20=20*=20Inval?= =?UTF-8?q?idate=20CutGizmo=20after=20changes=20in=20ObjectList=20or=20per?= =?UTF-8?q?form=20a=20cut=20=20=20*=20CupPlane=20in=20Connectors=20mode:?= =?UTF-8?q?=20Unselect=20selection,=20when=20click=20on=20empty=20space=20?= =?UTF-8?q?=20=20*=20Connectors=20mode:=20Fixed=20performance=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/icons/cut_connectors.svg | 26 +++++++++++++++ src/libslic3r/Model.cpp | 26 +++++++++++---- src/libslic3r/Model.hpp | 19 +++++++++-- src/slic3r/GUI/GUI_ObjectList.cpp | 44 +++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 40 +++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 4 +-- src/slic3r/GUI/ObjectDataViewModel.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 4 +++ 10 files changed, 123 insertions(+), 45 deletions(-) create mode 100644 resources/icons/cut_connectors.svg diff --git a/resources/icons/cut_connectors.svg b/resources/icons/cut_connectors.svg new file mode 100644 index 000000000..8cd03aa06 --- /dev/null +++ b/resources/icons/cut_connectors.svg @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index d4c81299f..79c54748a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1191,11 +1191,21 @@ size_t ModelObject::parts_count() const return num; } +bool ModelObject::has_connectors() const +{ + assert(is_cut()); + for (const ModelVolume* v : this->volumes) + if (v->cut_info.is_connector) + return true; + + return false; +} + indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes connector_attributes) { indexed_triangle_set connector_mesh; - int sectorCount; + int sectorCount {1}; switch (CutConnectorShape(connector_attributes.shape)) { case CutConnectorShape::Triangle: sectorCount = 3; @@ -1238,7 +1248,7 @@ void ModelObject::apply_cut_connectors(const std::string& new_name) new_volume->set_transformation(assemble_transform(connector.pos) * connector.rotation_m * scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); - new_volume->cut_info = { true, connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; + new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; new_volume->name = new_name + "-" + std::to_string(++connector_id); } cut_id.increase_connectors_cnt(cut_connectors.size()); @@ -1298,7 +1308,8 @@ void ModelVolume::reset_extra_facets() void ModelVolume::apply_tolerance() { - if (!cut_info.is_connector) + assert(cut_info.is_connector); + if (cut_info.is_processed) return; Vec3d sf = get_scaling_factor(); @@ -1321,7 +1332,8 @@ void ModelVolume::apply_tolerance() void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, std::vector& dowels, Vec3d& local_dowels_displace) { - volume->cut_info.discard(); + assert(volume->cut_info.is_connector); + volume->cut_info.set_processed(); const auto volume_matrix = volume->get_matrix(); @@ -1546,10 +1558,10 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix, volume->reset_extra_facets(); if (!volume->is_model_part()) { - if (volume->cut_info.is_connector) - process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace); - else + if (volume->cut_info.is_processed) process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower); + else + process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace); } else if (!volume->mesh().empty()) process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 16142ec05..cc2c612fe 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -477,6 +477,7 @@ public: int get_repaired_errors_count(const int vol_idx = -1) const; bool is_cut() const { return cut_id.id().valid(); } + bool has_connectors() const; private: friend class Model; @@ -723,14 +724,26 @@ public: struct CutInfo { bool is_connector{ false }; + bool is_processed{ true }; CutConnectorType connector_type{ CutConnectorType::Plug }; - float radius_tolerance;// [0.f : 1.f] - float height_tolerance;// [0.f : 1.f] + float radius_tolerance{ 0.f };// [0.f : 1.f] + float height_tolerance{ 0.f };// [0.f : 1.f] - void discard() { is_connector = false; } + CutInfo() = default; + CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance) : + is_connector(true), + is_processed(false), + connector_type(type), + radius_tolerance(rad_tolerance), + height_tolerance(h_tolerance) + {} + + void set_processed() { is_processed = true; } }; CutInfo cut_info; + bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } + // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } #if ENABLE_RAYCAST_PICKING diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 6227c5130..05eb02778 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1881,13 +1881,8 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type) mv->seam_facets.reset(); break; - case InfoItemType::Cut: - if (0) { // #ysFIXME_Cut - cnv->get_gizmos_manager().reset_all_states(); - Plater::TakeSnapshot(plater, _L("Remove cut connectors")); - (*m_objects)[obj_idx]->cut_connectors.clear(); - } else - Slic3r::GUI::show_error(nullptr, _L("Connectors cannot be deleted from cut object.")); + case InfoItemType::CutConnectors: + show_error(nullptr, _L("Connectors cannot be deleted from cut object.")); break; case InfoItemType::MmuSegmentation: @@ -2422,9 +2417,12 @@ bool ObjectList::is_splittable(bool to_objects) auto obj_idx = get_selected_obj_idx(); if (obj_idx < 0) return false; - if ((*m_objects)[obj_idx]->volumes.size() > 1) + const ModelObject* object = (*m_objects)[obj_idx]; + if (object->is_cut()) + return false; + if (object->volumes.size() > 1) return true; - return (*m_objects)[obj_idx]->volumes[0]->is_splittable(); + return object->volumes[0]->is_splittable(); } return false; } @@ -2595,19 +2593,13 @@ void ObjectList::part_selection_changed() } case InfoItemType::CustomSupports: case InfoItemType::CustomSeam: -// case InfoItemType::Cut: case InfoItemType::MmuSegmentation: { GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports : info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam : - info_type == InfoItemType::Cut ? GLGizmosManager::EType::Cut : GLGizmosManager::EType::MmuSegmentation; if (gizmos_mgr.get_current_type() != gizmo_type) gizmos_mgr.open_gizmo(gizmo_type); - if (info_type == InfoItemType::Cut) { - GLGizmoCut3D* cut = dynamic_cast(gizmos_mgr.get_current()); - cut->set_connectors_editing(); - } break; } case InfoItemType::Sinking: { break; } @@ -2762,7 +2754,7 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio for (InfoItemType type : {InfoItemType::CustomSupports, InfoItemType::CustomSeam, - InfoItemType::Cut, + InfoItemType::CutConnectors, InfoItemType::MmuSegmentation, InfoItemType::Sinking, InfoItemType::VariableLayerHeight}) { @@ -2783,11 +2775,8 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio }); break; - case InfoItemType::Cut : - if (0) // #ysFIXME_Cut - should_show = !model_object->cut_connectors.empty(); - else - should_show = model_object->is_cut() && model_object->volumes.size() > 1; + case InfoItemType::CutConnectors: + should_show = model_object->is_cut() && model_object->has_connectors() && model_object->volumes.size() > 1; break; case InfoItemType::VariableLayerHeight : should_show = printer_technology() == ptFFF @@ -2841,9 +2830,20 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) update_info_items(obj_idx, nullptr, call_selection_changed); + bool can_add_volumes = model_object->volumes.size() > 1; + if (can_add_volumes && model_object->is_cut()) { + int no_connectors_cnt = 0; + for (const ModelVolume* v : model_object->volumes) + if (!v->is_cut_connector()) + no_connectors_cnt++; + can_add_volumes = no_connectors_cnt > 1; + } + // add volumes to the object - if (model_object->volumes.size() > 1 && !model_object->is_cut()) { + if (can_add_volumes) { for (const ModelVolume* volume : model_object->volumes) { + if (model_object->is_cut() && volume->is_cut_connector()) + continue; const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item, from_u8(volume->name), volume->type(), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 1260ee3bb..97714ea62 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -18,9 +18,17 @@ namespace Slic3r { namespace GUI { -static const double Margin = 20.0; static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW(); +// connector colors +static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW(); +static const ColorRGBA DOWEL_COLOR = ColorRGBA::DARK_YELLOW(); +static const ColorRGBA HOVERED_PLAG_COLOR = ColorRGBA::CYAN(); +static const ColorRGBA HOVERED_DOWEL_COLOR = ColorRGBA(0.0f, 0.5f, 0.5f, 1.0f); +static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY(); +static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::DARK_GRAY(); +static const ColorRGBA CONNECTOR_DEF_COLOR = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); + const unsigned int AngleResolution = 64; const unsigned int ScaleStepsCount = 72; const float ScaleStepRad = 2.0f * float(PI) / ScaleStepsCount; @@ -1426,8 +1434,6 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) unselect_all_connectors(); set_connectors_editing(false); } - - m_parent.request_extra_frame(); } void GLGizmoCut3D::render_build_size() @@ -1455,10 +1461,22 @@ void GLGizmoCut3D::reset_cut_plane() update_clipper(); } +void GLGizmoCut3D::invalidate_cut_plane() +{ + m_rotation_m = Transform3d::Identity(); + m_plane_center = Vec3d::Zero(); + m_min_pos = Vec3d::Zero(); + m_max_pos = Vec3d::Zero(); + m_bb_center = Vec3d::Zero(); + m_center_offset = Vec3d::Zero(); +} + void GLGizmoCut3D::set_connectors_editing(bool connectors_editing) { m_connectors_editing = connectors_editing; update_raycasters_for_picking(); + + m_parent.request_extra_frame(); } void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) @@ -1661,13 +1679,14 @@ void GLGizmoCut3D::render_connectors() pos[Z] += sla_shift; // First decide about the color of the point. - if (size_t(m_hover_id- m_connectors_group_id) == i) - render_color = ColorRGBA::CYAN(); + if (!m_connectors_editing) + render_color = CONNECTOR_DEF_COLOR; + else if (size_t(m_hover_id - m_connectors_group_id) == i) + render_color = connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR; else if (m_selected[i]) - render_color = ColorRGBA::DARK_GRAY(); + render_color = connector.attribs.type == CutConnectorType::Dowel ? SELECTED_DOWEL_COLOR : SELECTED_PLAG_COLOR; else // neither hover nor picking - render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); - + render_color = connector.attribs.type == CutConnectorType::Dowel ? DOWEL_COLOR : PLAG_COLOR; // ! #ysFIXME rework get_volume_transformation if (0) { // else { // neither hover nor picking int mesh_id = -1; @@ -1773,6 +1792,8 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) else { // the object is SLA-elevated and the plane is under it. } + + invalidate_cut_plane(); } @@ -2005,7 +2026,8 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi if (action == SLAGizmoEventType::LeftDown && !shift_down) { // If there is no selection and no hovering, add new point if (m_hover_id == -1 && !control_down && !alt_down) - return add_connector(connectors, mouse_position); + if (!add_connector(connectors, mouse_position)) + unselect_all_connectors(); return true; } if (!m_connectors_editing) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 2df337b04..fe8422865 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -149,6 +149,7 @@ public: void update_clipper(); void update_clipper_on_render(); void set_connectors_editing() { m_connectors_editing = true; } + void invalidate_cut_plane(); BoundingBoxf3 bounding_box() const; BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 16433bc79..594e0716c 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1228,7 +1228,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d object was loaded with multimaterial painting.", "%1$d objects were loaded with multimaterial painting.",(*it).second), (*it).second) + "\n"; break; case InfoItemType::VariableLayerHeight: text += format(_L_PLURAL("%1$d object was loaded with variable layer height.", "%1$d objects were loaded with variable layer height.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::Sinking: text += format(_L_PLURAL("%1$d object was loaded with partial sinking.", "%1$d objects were loaded with partial sinking.", (*it).second), (*it).second) + "\n"; break; - case InfoItemType::Cut: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; + case InfoItemType::CutConnectors: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break; } } diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 61d5bf6ec..4c592b878 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -38,7 +38,7 @@ static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; static constexpr char WarningManifoldIcon[] = "exclamation_manifold"; -static constexpr char LockIcon[] = "lock_closed"; +static constexpr char LockIcon[] = "cut_"; struct InfoItemAtributes { std::string name; @@ -49,7 +49,7 @@ const std::map INFO_ITEMS{ // info_item Type info_item Name info_item BitmapName { InfoItemType::CustomSupports, {L("Paint-on supports"), "fdm_supports_" }, }, { InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, }, - { InfoItemType::Cut, {L("Cut connectors"), "cut_" }, }, + { InfoItemType::CutConnectors, {L("Cut connectors"), "cut_connectors" }, }, { InfoItemType::MmuSegmentation, {L("Multimaterial painting"), "mmu_segmentation_"}, }, { InfoItemType::Sinking, {L("Sinking"), "sinking"}, }, { InfoItemType::VariableLayerHeight, {L("Variable layer height"), "layers"}, }, diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 87be7a4e2..bc9144803 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -51,7 +51,7 @@ enum class InfoItemType Undef, CustomSupports, CustomSeam, - Cut, + CutConnectors, MmuSegmentation, Sinking, VariableLayerHeight diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d09de9cf3..bb3f6e41a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -97,6 +97,7 @@ #include "MsgDialog.hpp" #include "ProjectDirtyStateManager.hpp" #include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification +#include "Gizmos/GLGizmoCut.hpp" #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -2980,6 +2981,9 @@ void Plater::priv::object_list_changed() const bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() == ModelInstancePVS_Inside; sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); + + // invalidate CutGizmo after changes in ObjectList + static_cast(q->canvas3D()->get_gizmos_manager().get_gizmo(GLGizmosManager::Cut))->invalidate_cut_plane(); } void Plater::priv::select_all() From d1c871758b72ec7a683df01de4907169978b4785 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 29 Sep 2022 12:26:08 +0200 Subject: [PATCH 77/93] Cut WIP: * ObjectDataViewModel: Respect to the volume id, when adding the new volume to the object * 3mf : Save/Load info about connectors --- src/libslic3r/Format/3mf.cpp | 90 ++++++++++++++++++++------ src/libslic3r/Model.hpp | 4 +- src/slic3r/GUI/GUI_ObjectList.hpp | 5 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 38 +++-------- src/slic3r/GUI/ObjectDataViewModel.hpp | 12 ++-- 5 files changed, 93 insertions(+), 56 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 4251110c4..f2056b8bd 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -409,6 +409,19 @@ namespace Slic3r { VolumeMetadataList volumes; }; + struct CutObjectInfo + { + struct Connector + { + int volume_id; + int type; + float r_tolerance; + float h_tolerance; + }; + CutObjectBase id; + std::vector connectors; + }; + // Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects. typedef std::map IdToModelObjectMap; typedef std::map IdToAliasesMap; @@ -417,7 +430,7 @@ namespace Slic3r { typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; - typedef std::map IdToCutObjectIdMap; + typedef std::map IdToCutObjectInfoMap; typedef std::map> IdToSlaSupportPointsMap; typedef std::map> IdToSlaDrainHolesMap; @@ -445,7 +458,7 @@ namespace Slic3r { IdToGeometryMap m_geometries; CurrentConfig m_curr_config; IdToMetadataMap m_objects_metadata; - IdToCutObjectIdMap m_cut_object_ids; + IdToCutObjectInfoMap m_cut_object_infos; IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerConfigRangesMap m_layer_config_ranges; IdToSlaSupportPointsMap m_sla_support_points; @@ -774,11 +787,6 @@ namespace Slic3r { return false; } - // m_cut_object_ids are indexed by a 1 based model object index. - IdToCutObjectIdMap::iterator cut_object_id = m_cut_object_ids.find(object.second + 1); - if (cut_object_id != m_cut_object_ids.end()) - model_object->cut_id = std::move(cut_object_id->second); - // m_layer_heights_profiles are indexed by a 1 based model object index. IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1); if (obj_layer_heights_profile != m_layer_heights_profiles.end()) @@ -831,6 +839,19 @@ namespace Slic3r { if (!_generate_volumes(*model_object, obj_geometry->second, *volumes_ptr, config_substitutions)) return false; + + // Apply cut information for object if any was loaded + // m_cut_object_ids are indexed by a 1 based model object index. + IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1); + if (cut_object_info != m_cut_object_infos.end()) { + model_object->cut_id = cut_object_info->second.id; + + for (auto connector : cut_object_info->second.connectors) { + assert(0 <= connector.volume_id && connector.volume_id <= int(model_object->volumes.size())); + model_object->volumes[connector.volume_id]->cut_info = + ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true); + } + } } // If instances contain a single volume, the volume offset should be 0,0,0 @@ -979,22 +1000,39 @@ namespace Slic3r { continue; } - IdToCutObjectIdMap::iterator object_item = m_cut_object_ids.find(obj_idx); - if (object_item != m_cut_object_ids.end()) { + IdToCutObjectInfoMap::iterator object_item = m_cut_object_infos.find(obj_idx); + if (object_item != m_cut_object_infos.end()) { add_error("Found duplicated cut_object_id"); continue; } - for (const auto& obj_cut_id : object_tree) { - if (obj_cut_id.first != "cut_id") - continue; - pt::ptree cut_id_tree = obj_cut_id.second; - ObjectID obj_id(cut_id_tree.get(".id")); - CutObjectBase cut_id(ObjectID(cut_id_tree.get(".id")), - cut_id_tree.get(".check_sum"), - cut_id_tree.get(".connectors_cnt")); - m_cut_object_ids.insert({ obj_idx, std::move(cut_id) }); + CutObjectBase cut_id; + std::vector connectors; + + for (const auto& obj_cut_info : object_tree) { + if (obj_cut_info.first == "cut_id") { + pt::ptree cut_id_tree = obj_cut_info.second; + cut_id = CutObjectBase(ObjectID( cut_id_tree.get(".id")), + cut_id_tree.get(".check_sum"), + cut_id_tree.get(".connectors_cnt")); + } + if (obj_cut_info.first == "connectors") { + pt::ptree cut_connectors_tree = obj_cut_info.second; + for (const auto& cut_connector : cut_connectors_tree) { + if (cut_connector.first != "connector") + continue; + pt::ptree connector_tree = cut_connector.second; + CutObjectInfo::Connector connector = {connector_tree.get(".volume_id"), + connector_tree.get(".type"), + connector_tree.get(".r_tolerance"), + connector_tree.get(".h_tolerance")}; + connectors.emplace_back(connector); + } + } } + + CutObjectInfo cut_info {cut_id, connectors}; + m_cut_object_infos.insert({ obj_idx, cut_info }); } } } @@ -2865,6 +2903,18 @@ namespace Slic3r { cut_id_tree.put(".id", object->cut_id.id().id); cut_id_tree.put(".check_sum", object->cut_id.check_sum()); cut_id_tree.put(".connectors_cnt", object->cut_id.connectors_cnt()); + + int volume_idx = -1; + for (const ModelVolume* volume : object->volumes) { + ++volume_idx; + if (volume->is_cut_connector()) { + pt::ptree& connectors_tree = obj_tree.add("connectors.connector", ""); + connectors_tree.put(".volume_id", volume_idx); + connectors_tree.put(".type", int(volume->cut_info.connector_type)); + connectors_tree.put(".r_tolerance", volume->cut_info.radius_tolerance); + connectors_tree.put(".h_tolerance", volume->cut_info.height_tolerance); + } + } } if (!tree.empty()) { @@ -2876,6 +2926,10 @@ namespace Slic3r { boost::replace_all(out, ">\n \n ", ">\n "); + boost::replace_all(out, ">\n ", ">\n "); + boost::replace_all(out, ">\n ", ">\n "); boost::replace_all(out, ">", ">\n "); // OR just boost::replace_all(out, "><", ">\n<"); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index cc2c612fe..01cd37414 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -730,9 +730,9 @@ public: float height_tolerance{ 0.f };// [0.f : 1.f] CutInfo() = default; - CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance) : + CutInfo(CutConnectorType type, float rad_tolerance, float h_tolerance, bool processed = false) : is_connector(true), - is_processed(false), + is_processed(processed), connector_type(type), radius_tolerance(rad_tolerance), height_tolerance(h_tolerance) diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 79f8e74c6..3ae0d0533 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -288,6 +288,9 @@ public: void changed_object(const int obj_idx = -1) const; void part_selection_changed(); + // Add object's volumes to the list + // Return selected items, if add_to_selection is defined + wxDataViewItemArray add_volumes_to_object_in_list(size_t obj_idx, std::function add_to_selection = nullptr); // Add object to the list void add_object_to_list(size_t obj_idx, bool call_selection_changed = true); // Delete object from the list @@ -392,7 +395,7 @@ public: void toggle_printable_state(); void set_extruder_for_selected_items(const int extruder) const ; - wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection = nullptr); + wxDataViewItemArray reorder_volumes_and_get_selection(size_t obj_idx, std::function add_to_selection = nullptr); void apply_volumes_order(); bool has_paint_on_segmentation(); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 4c592b878..0475fe395 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -373,13 +373,12 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node, con UpdateBitmapForNode(node); } -wxDataViewItem ObjectDataViewModel::Add(const wxString &name, - const int extruder, +wxDataViewItem ObjectDataViewModel::AddObject(const wxString &name, + const wxString& extruder, const std::string& warning_icon_name, const bool has_lock) { - const wxString extruder_str = extruder == 0 ? _L("default") : wxString::Format("%d", extruder); - auto root = new ObjectDataViewModelNode(name, extruder_str); + auto root = new ObjectDataViewModelNode(name, extruder); // Add warning icon if detected auto-repaire UpdateBitmapForNode(root, warning_icon_name, has_lock); @@ -394,37 +393,20 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name, wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item, const wxString &name, + const int volume_idx, const Slic3r::ModelVolumeType volume_type, - const std::string& warning_icon_name/* = std::string()*/, - const int extruder/* = 0*/, - const bool create_frst_child/* = true*/) + const std::string& warning_icon_name, + const wxString& extruder) { ObjectDataViewModelNode *root = static_cast(parent_item.GetID()); if (!root) return wxDataViewItem(0); - wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); - // get insertion position according to the existed Layers and/or Instances Items int insert_position = get_root_idx(root, itLayerRoot); if (insert_position < 0) insert_position = get_root_idx(root, itInstanceRoot); - if (create_frst_child && root->m_volumes_cnt == 0) - { - const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART; - const auto node = new ObjectDataViewModelNode(root, root->m_name, type, extruder_str, 0); - UpdateBitmapForNode(node, root->warning_icon_name(), root->has_lock()); - - insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - - root->m_volumes_cnt++; - if (insert_position >= 0) insert_position++; - } - - const auto node = new ObjectDataViewModelNode(root, name, volume_type, extruder_str, root->m_volumes_cnt); + const auto node = new ObjectDataViewModelNode(root, name, volume_type, extruder, volume_idx); UpdateBitmapForNode(node, warning_icon_name, root->has_lock() && volume_type < ModelVolumeType::PARAMETER_MODIFIER); insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position); @@ -631,14 +613,12 @@ wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_i wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item, const t_layer_height_range& layer_range, - const int extruder/* = 0*/, + const wxString& extruder, const int index /* = -1*/) { ObjectDataViewModelNode *parent_node = static_cast(parent_item.GetID()); if (!parent_node) return wxDataViewItem(0); - wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); - // get LayerRoot node ObjectDataViewModelNode *layer_root_node; wxDataViewItem layer_root_item; @@ -655,7 +635,7 @@ wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_ } // Add layer node - ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str); + ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder); if (index < 0) layer_root_node->Append(layer_node); else diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index bc9144803..55dbaafe2 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -271,16 +271,16 @@ public: ObjectDataViewModel(); ~ObjectDataViewModel(); - wxDataViewItem Add( const wxString &name, - const int extruder, + wxDataViewItem AddObject( const wxString &name, + const wxString& extruder, const std::string& warning_icon_name, const bool has_lock); wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item, const wxString &name, + const int volume_idx, const Slic3r::ModelVolumeType volume_type, - const std::string& warning_icon_name = std::string(), - const int extruder = 0, - const bool create_frst_child = true); + const std::string& warning_icon_name, + const wxString& extruder); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); wxDataViewItem AddInfoChild(const wxDataViewItem &parent_item, InfoItemType info_type); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); @@ -288,7 +288,7 @@ public: wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item, const t_layer_height_range& layer_range, - const int extruder = 0, + const wxString& extruder, const int index = -1); size_t GetItemIndexForFirstVolume(ObjectDataViewModelNode* node_parent); wxDataViewItem Delete(const wxDataViewItem &item); From a8440db5ec6cff6319a8bb7b9824a416d1414be4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 30 Sep 2022 14:07:17 +0200 Subject: [PATCH 78/93] Cut WIP: * ObjectList & Selection: Show Connectors in the Scene, when CutConnectors Item is selected * ObjectList: refactoring: extract the adding of volumes to the add_volumes_to_object_in_list() * If some connector is selected on 3dScene -> select all connectors of this object * GLGizmoScale3D : check if grabber is enabled, when do rendering + GLGizmoCut: refactoring : split render_cut_plane_grabbers to several functions --- src/slic3r/GUI/GUI_ObjectList.cpp | 235 +++++++++++++--------- src/slic3r/GUI/GUI_ObjectList.hpp | 2 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 204 +++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 13 +- src/slic3r/GUI/Selection.cpp | 22 ++ src/slic3r/GUI/Selection.hpp | 2 + 10 files changed, 268 insertions(+), 232 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 05eb02778..d3f1d1bf2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -403,6 +403,13 @@ MeshErrorsInfo ObjectList::get_mesh_errors_info(const int obj_idx, const int vol if (obj_idx < 0) return { {}, {} }; // hide tooltip + const ModelObject* object = (*m_objects)[obj_idx]; + if (vol_idx != -1 && vol_idx >= int(object->volumes.size())) { + if (sidebar_info) + *sidebar_info = _L("Wrong volume index "); + return { {}, {} }; // hide tooltip + } + const TriangleMeshStats& stats = vol_idx == -1 ? (*m_objects)[obj_idx]->get_object_stl_stats() : (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats(); @@ -2075,30 +2082,11 @@ void ObjectList::split() volume->split(nozzle_dmrs_cnt); + (*m_objects)[obj_idx]->input_file.clear(); + wxBusyCursor wait; - auto model_object = (*m_objects)[obj_idx]; - - auto parent = m_objects_model->GetTopParent(item); - if (parent) - m_objects_model->DeleteVolumeChildren(parent); - else - parent = item; - - for (const ModelVolume* volume : model_object->volumes) { - const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name), - volume->type(),// is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART, - get_warning_icon_name(volume->mesh().stats()), - volume->config.has("extruder") ? volume->config.extruder() : 0, - false); - // add settings to the part, if it has those - add_settings_item(vol_item, &volume->config.get()); - } - - model_object->input_file.clear(); - - if (parent == item) - Expand(parent); + add_volumes_to_object_in_list(obj_idx); changed_object(obj_idx); // update printable state for new volumes on canvas3D @@ -2524,7 +2512,14 @@ void ObjectList::part_selection_changed() GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager(); - if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { + if (item && m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) { + og_name = _L("Cut Connectors information"); + + update_and_show_manipulations = true; + enable_manipulation = false; + disable_ununiform_scale = true; + } + else if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { og_name = _L("Group manipulation"); const Selection& selection = scene_selection(); @@ -2575,11 +2570,12 @@ void ObjectList::part_selection_changed() const wxDataViewItem parent = m_objects_model->GetParent(item); const ItemType parent_type = m_objects_model->GetItemType(parent); obj_idx = m_objects_model->GetObjectIdByItem(item); + ModelObject* object = (*m_objects)[obj_idx]; if (parent == wxDataViewItem(nullptr) || type == itInfo) { og_name = _L("Object manipulation"); - m_config = &(*m_objects)[obj_idx]->config; + m_config = &object->config; update_and_show_manipulations = true; if (type == itInfo) { @@ -2602,23 +2598,23 @@ void ObjectList::part_selection_changed() gizmos_mgr.open_gizmo(gizmo_type); break; } - case InfoItemType::Sinking: { break; } + case InfoItemType::Sinking: default: { break; } } } else - disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + disable_ss_manipulation = object->is_cut(); } else { if (type & itSettings) { if (parent_type & itObject) { og_name = _L("Object Settings to modify"); - m_config = &(*m_objects)[obj_idx]->config; + m_config = &object->config; } else if (parent_type & itVolume) { og_name = _L("Part Settings to modify"); volume_id = m_objects_model->GetVolumeIdByItem(parent); - m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; + m_config = &object->volumes[volume_id]->config; } else if (parent_type & itLayer) { og_name = _L("Layer range Settings to modify"); @@ -2629,17 +2625,17 @@ void ObjectList::part_selection_changed() else if (type & itVolume) { og_name = _L("Part manipulation"); volume_id = m_objects_model->GetVolumeIdByItem(item); - m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; + m_config = &object->volumes[volume_id]->config; update_and_show_manipulations = true; - enable_manipulation = !(*m_objects)[obj_idx]->is_cut(); + enable_manipulation = !(object->is_cut() && object->volumes[volume_id]->is_cut_connector()); } else if (type & itInstance) { og_name = _L("Instance manipulation"); update_and_show_manipulations = true; // fill m_config by object's values - m_config = &(*m_objects)[obj_idx]->config; - disable_ss_manipulation = (*m_objects)[obj_idx]->is_cut(); + m_config = &object->config; + disable_ss_manipulation = object->is_cut(); } else if (type & (itLayerRoot|itLayer)) { og_name = type & itLayerRoot ? _L("Height ranges") : _L("Settings for height range"); @@ -2658,7 +2654,6 @@ void ObjectList::part_selection_changed() wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " "); if (item) { - // wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item)); wxGetApp().obj_manipul()->update_item_name(m_objects_model->GetName(item)); wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_info(obj_idx, volume_id)); } @@ -2817,43 +2812,70 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio } } +static wxString extruder2str(int extruder) +{ + return extruder == 0 ? _L("default") : wxString::Format("%d", extruder); +} +static bool can_add_volumes_to_object(const ModelObject* object) +{ + bool can = object->volumes.size() > 1; + + if (can && object->is_cut()) { + int no_connectors_cnt = 0; + for (const ModelVolume* v : object->volumes) + if (!v->is_cut_connector()) + no_connectors_cnt++; + can = no_connectors_cnt > 1; + } + + return can; +} + +wxDataViewItemArray ObjectList::add_volumes_to_object_in_list(size_t obj_idx, std::function add_to_selection/* = nullptr*/) +{ + wxDataViewItem object_item = m_objects_model->GetItemById(int(obj_idx)); + m_objects_model->DeleteVolumeChildren(object_item); + + wxDataViewItemArray items; + + const ModelObject* object = (*m_objects)[obj_idx]; + // add volumes to the object + if (can_add_volumes_to_object(object)) { + int volume_idx{ -1 }; + for (const ModelVolume* volume : object->volumes) { + ++volume_idx; + if (object->is_cut() && volume->is_cut_connector()) + continue; + const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, + from_u8(volume->name), + volume_idx, + volume->type(), + get_warning_icon_name(volume->mesh().stats()), + extruder2str(volume->config.has("extruder") ? volume->config.extruder() : 0)); + add_settings_item(vol_item, &volume->config.get()); + + if (add_to_selection && add_to_selection(volume)) + items.Add(vol_item); + } + Expand(object_item); + } + + return items; +} void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) { auto model_object = (*m_objects)[obj_idx]; const wxString& item_name = from_u8(model_object->name); - const auto item = m_objects_model->Add(item_name, - model_object->config.has("extruder") ? model_object->config.extruder() : 0, + const auto item = m_objects_model->AddObject(item_name, + extruder2str(model_object->config.has("extruder") ? model_object->config.extruder() : 0), get_warning_icon_name(model_object->mesh().stats()), model_object->is_cut()); update_info_items(obj_idx, nullptr, call_selection_changed); - bool can_add_volumes = model_object->volumes.size() > 1; - if (can_add_volumes && model_object->is_cut()) { - int no_connectors_cnt = 0; - for (const ModelVolume* v : model_object->volumes) - if (!v->is_cut_connector()) - no_connectors_cnt++; - can_add_volumes = no_connectors_cnt > 1; - } - - // add volumes to the object - if (can_add_volumes) { - for (const ModelVolume* volume : model_object->volumes) { - if (model_object->is_cut() && volume->is_cut_connector()) - continue; - const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item, - from_u8(volume->name), - volume->type(), - get_warning_icon_name(volume->mesh().stats()), - volume->config.has("extruder") ? volume->config.extruder() : 0, - false); - add_settings_item(vol_item, &volume->config.get()); - } - Expand(item); - } + add_volumes_to_object_in_list(obj_idx); // add instances to the object, if it has those if (model_object->instances.size()>1) @@ -3327,7 +3349,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range, const auto layer_item = m_objects_model->AddLayersChild(layers_item, range, - config.opt_int("extruder"), + extruder2str(config.opt_int("extruder")), layer_idx); add_settings_item(layer_item, &config); } @@ -3421,6 +3443,24 @@ bool ObjectList::is_selected(const ItemType type) const return false; } +bool ObjectList::is_connectors_item_selected() const +{ + const wxDataViewItem& item = GetSelection(); + if (item) + return m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors; + + return false; +} + +bool ObjectList::is_connectors_item_selected(const wxDataViewItemArray& sels) const +{ + for (auto item : sels) + if (m_objects_model->GetItemType(item) == itInfo && m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) + return true; + + return false; +} + int ObjectList::get_selected_layers_range_idx() const { const wxDataViewItem& item = GetSelection(); @@ -3547,11 +3587,18 @@ void ObjectList::update_selections() else { for (auto idx : selection.get_volume_idxs()) { const auto gl_vol = selection.get_volume(idx); - if (gl_vol->volume_idx() >= 0) + if (gl_vol->volume_idx() >= 0) { // Only add GLVolumes with non-negative volume_ids. GLVolumes with negative volume ids // are not associated with ModelVolumes, but they are temporarily generated by the backend // (for example, SLA supports or SLA pad). - sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx())); + int obj_idx = gl_vol->object_idx(); + int vol_idx = gl_vol->volume_idx(); + assert(obj_idx >= 0 && vol_idx >= 0); + if (object(obj_idx)->volumes[vol_idx]->is_cut_connector()) + sels.Add(m_objects_model->GetInfoItemByType(m_objects_model->GetItemById(obj_idx), InfoItemType::CutConnectors)); + else + sels.Add(m_objects_model->GetItemByVolumeId(obj_idx, vol_idx)); + } } m_selection_mode = smVolume; } } @@ -3602,7 +3649,7 @@ void ObjectList::update_selections() if (sels.size() == 0 || m_selection_mode & smSettings) m_selection_mode = smUndef; - if (fix_cut_selection(sels)) { + if (fix_cut_selection(sels) || is_connectors_item_selected(sels)) { m_prevent_list_events = true; // If some part is selected, unselect all items except of selected parts of the current object @@ -3616,7 +3663,7 @@ void ObjectList::update_selections() update_selections_on_canvas(); // to update the toolbar and info sizer - if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { + if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject || is_connectors_item_selected()) { auto event = SimpleEvent(EVT_OBJ_LIST_OBJECT_SELECT); event.SetEventObject(this); wxPostEvent(this, event); @@ -3662,16 +3709,29 @@ void ObjectList::update_selections_on_canvas() volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); } else if (type == itInfo) { - // When selecting an info item, select one instance of the - // respective object - a gizmo may want to be opened. - int inst_idx = selection.get_instance_idx(); - int scene_obj_idx = selection.get_object_idx(); - mode = Selection::Instance; - // select first instance, unless an instance of the object is already selected - if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx) - inst_idx = 0; - std::vector idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx); - volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) { + mode = Selection::Volume; + + // When selecting CutConnectors info item, select all object volumes, which are marked as a connector + const ModelObject* obj = object(obj_idx); + for (unsigned int vol_idx = 0; vol_idx < obj->volumes.size(); vol_idx++) + if (obj->volumes[vol_idx]->is_cut_connector()) { + std::vector idxs = selection.get_volume_idxs_from_volume(obj_idx, std::max(instance_idx, 0), vol_idx); + volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + } + } + else { + // When selecting an info item, select one instance of the + // respective object - a gizmo may want to be opened. + int inst_idx = selection.get_instance_idx(); + int scene_obj_idx = selection.get_object_idx(); + mode = Selection::Instance; + // select first instance, unless an instance of the object is already selected + if (scene_obj_idx == -1 || inst_idx == -1 || scene_obj_idx != obj_idx) + inst_idx = 0; + std::vector idxs = selection.get_volume_idxs_from_instance(obj_idx, inst_idx); + volume_idxs.insert(volume_idxs.end(), idxs.begin(), idxs.end()); + } } else { @@ -4569,33 +4629,14 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const wxGetApp().plater()->update(); } -wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection/* = nullptr*/) +wxDataViewItemArray ObjectList::reorder_volumes_and_get_selection(size_t obj_idx, std::function add_to_selection/* = nullptr*/) { - wxDataViewItemArray items; + (*m_objects)[obj_idx]->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); - ModelObject* object = (*m_objects)[obj_idx]; - if (object->volumes.size() <= 1) - return items; + wxDataViewItemArray items = add_volumes_to_object_in_list(obj_idx, std::move(add_to_selection)); - object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1"); + changed_object(int(obj_idx)); - wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx); - m_objects_model->DeleteVolumeChildren(object_item); - - for (const ModelVolume* volume : object->volumes) { - wxDataViewItem vol_item = m_objects_model->AddVolumeChild(object_item, from_u8(volume->name), - volume->type(), - get_warning_icon_name(volume->mesh().stats()), - volume->config.has("extruder") ? volume->config.extruder() : 0, - false); - // add settings to the part, if it has those - add_settings_item(vol_item, &volume->config.get()); - - if (add_to_selection && add_to_selection(volume)) - items.Add(vol_item); - } - - changed_object(obj_idx); return items; } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 3ae0d0533..cb498f87e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -343,6 +343,8 @@ public: void init_objects(); bool multiple_selection() const ; bool is_selected(const ItemType type) const; + bool is_connectors_item_selected() const; + bool is_connectors_item_selected(const wxDataViewItemArray& sels) const; int get_selected_layers_range_idx() const; void set_selected_layers_range_idx(const int range_idx) { m_selected_layers_range_idx = range_idx; } void set_selection_mode(SELECTION_MODE mode) { m_selection_mode = mode; } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index e54a4cf56..a2d687db0 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -763,7 +763,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection) #endif // ENABLE_WORLD_COORDINATE m_new_enabled = true; } - else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) { + else if (obj_list->is_connectors_item_selected() || obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) { reset_settings_value(); m_new_move_label_string = L("Translate"); m_new_rotate_label_string = L("Rotate"); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 97714ea62..71a9c5dcf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -597,114 +597,109 @@ static float get_grabber_mean_size(const BoundingBoxf3& bb) return float((bb.size().x() + bb.size().y() + bb.size().z()) / 3.0); } -void GLGizmoCut3D::render_cut_center_grabber() +void GLGizmoCut3D::render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix) +{ + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader) { + shader->start_using(); + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", wxGetApp().plater()->get_camera().get_projection_matrix()); + + model.set_color(color); + model.render(); + + shader->stop_using(); + } +} + +void GLGizmoCut3D::render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width) +{ + GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); + if (shader) { + shader->start_using(); + + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", wxGetApp().plater()->get_camera().get_projection_matrix()); + shader->set_uniform("width", width); + + line_model.set_color(color); + line_model.render(); + + shader->stop_using(); + } +} + +void GLGizmoCut3D::render_rotation_snapping(Axis axis, const ColorRGBA& color) +{ + GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); + if (!line_shader) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_start_dragging_m; + + if (axis == X) + view_model_matrix = view_model_matrix * rotation_transform(0.5 * PI * Vec3d::UnitY()) * rotation_transform(-PI * Vec3d::UnitZ()); + else + view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * rotation_transform(-0.5 * PI * Vec3d::UnitY()); + + line_shader->start_using(); + line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + line_shader->set_uniform("view_model_matrix", view_model_matrix); + line_shader->set_uniform("width", 0.25f); + + m_circle.render(); + m_scale.render(); + m_snap_radii.render(); + m_reference_radius.render(); + if (m_dragging) { + line_shader->set_uniform("width", 1.5f); + m_angle_arc.set_color(color); + m_angle_arc.render(); + } + + line_shader->stop_using(); +} + +void GLGizmoCut3D::render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix) +{ + const Transform3d line_view_matrix = view_matrix * scale_transform(Vec3d(1.0, 1.0, m_grabber_connection_len)); + + render_line(m_grabber_connection, color, line_view_matrix, 0.2f); +}; + +void GLGizmoCut3D::render_cut_plane_grabbers() { glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (!shader) - return; - ColorRGBA color = m_hover_id == Z ? complementary(GRABBER_COLOR) : GRABBER_COLOR; - const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + const Grabber& grabber = m_grabbers.front(); + const float mean_size = get_grabber_mean_size(bounding_box()); - const BoundingBoxf3 box = bounding_box(); - - const float mean_size = get_grabber_mean_size(box); double size = m_dragging && m_hover_id == Z ? double(grabber.get_dragging_half_size(mean_size)) : double(grabber.get_half_size(mean_size)); Vec3d cone_scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); Vec3d offset = 1.25 * size * Vec3d::UnitZ(); - shader->start_using(); - shader->set_uniform("emission_factor", 0.1f); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - const Transform3d view_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - - auto render = [shader, this](GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix) { - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - model.set_color(color); - model.render(); - }; - - auto render_grabber_connection = [shader, camera, view_matrix, this](const ColorRGBA& color) - { - shader->stop_using(); - GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); - if (!line_shader) - return; - - line_shader->start_using(); - line_shader->set_uniform("emission_factor", 0.1f); - line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - const Transform3d trafo = view_matrix * scale_transform(Vec3d(1.0, 1.0, m_grabber_connection_len)); - line_shader->set_uniform("view_model_matrix", trafo); - line_shader->set_uniform("normal_matrix", (Matrix3d)trafo.matrix().block(0, 0, 3, 3).inverse().transpose()); - line_shader->set_uniform("width", 0.2f); - - m_grabber_connection.set_color(color); - m_grabber_connection.render(); - - line_shader->stop_using(); - shader->start_using(); - }; - - auto render_rotation_snapping = [shader, camera, this](Axis axis, const ColorRGBA& color) - { - Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_start_dragging_m; - - if (axis == X) - view_model_matrix = view_model_matrix * rotation_transform( 0.5 * PI * Vec3d::UnitY()) * rotation_transform(-PI * Vec3d::UnitZ()); - else - view_model_matrix = view_model_matrix * rotation_transform(-0.5 * PI * Vec3d::UnitZ()) * rotation_transform(-0.5 * PI * Vec3d::UnitY()); - - shader->stop_using(); - - GLShaderProgram* line_shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); - if (!line_shader) - return; - - line_shader->start_using(); - line_shader->set_uniform("emission_factor", 0.1f); - line_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - line_shader->set_uniform("view_model_matrix", view_model_matrix); - line_shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); - line_shader->set_uniform("width", 0.25f); - - m_circle.render(); - m_scale.render(); - m_snap_radii.render(); - m_reference_radius.render(); - if (m_dragging) { - line_shader->set_uniform("width", 1.5f); - m_angle_arc.set_color(color); - m_angle_arc.render(); - } - - line_shader->stop_using(); - shader->start_using(); - }; - // render Z grabber if ((!m_dragging && m_hover_id < 0)) - render_grabber_connection(color); - render(m_sphere.model, color, view_matrix * scale_transform(size)); + render_grabber_connection(color, view_matrix); + render_model(m_sphere.model, color, view_matrix * scale_transform(size)); if (!m_dragging && m_hover_id < 0 || m_hover_id == Z) { const BoundingBoxf3 tbb = transformed_bounding_box(); if (tbb.min.z() <= 0.0) - render(m_cone.model, color, view_matrix * assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); + render_model(m_cone.model, color, view_matrix * assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); if (tbb.max.z() >= 0.0) - render(m_cone.model, color, view_matrix * assemble_transform(offset, Vec3d::Zero(), cone_scale)); + render_model(m_cone.model, color, view_matrix * assemble_transform(offset, Vec3d::Zero(), cone_scale)); } // render top sphere for X/Y grabbers @@ -714,7 +709,7 @@ void GLGizmoCut3D::render_cut_center_grabber() size = m_dragging ? double(grabber.get_dragging_half_size(mean_size)) : double(grabber.get_half_size(mean_size)); color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::GRAY(); - render(m_sphere.model, color, view_matrix * assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); + render_model(m_sphere.model, color, view_matrix * assemble_transform(m_grabber_connection_len * Vec3d::UnitZ(), Vec3d::Zero(), size * Vec3d::Ones())); } // render X grabber @@ -726,14 +721,14 @@ void GLGizmoCut3D::render_cut_center_grabber() color = m_hover_id == X ? complementary(ColorRGBA::RED()) : ColorRGBA::RED(); if (m_hover_id == X) { - render_grabber_connection(color); + render_grabber_connection(color, view_matrix); render_rotation_snapping(X, color); } offset = Vec3d(0.0, 1.25 * size, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); + render_model(m_cone.model, color, view_matrix * assemble_transform(offset, -0.5 * PI * Vec3d::UnitX(), cone_scale)); offset = Vec3d(0.0, -1.25 * size, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); + render_model(m_cone.model, color, view_matrix * assemble_transform(offset, 0.5 * PI * Vec3d::UnitX(), cone_scale)); } // render Y grabber @@ -745,17 +740,15 @@ void GLGizmoCut3D::render_cut_center_grabber() color = m_hover_id == Y ? complementary(ColorRGBA::GREEN()) : ColorRGBA::GREEN(); if (m_hover_id == Y) { - render_grabber_connection(color); + render_grabber_connection(color, view_matrix); render_rotation_snapping(Y, color); } offset = Vec3d(1.25 * size, 0.0, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); + render_model(m_cone.model, color, view_matrix * assemble_transform(offset, 0.5 * PI * Vec3d::UnitY(), cone_scale)); offset = Vec3d(-1.25 * size, 0.0, m_grabber_connection_len); - render(m_cone.model, color, view_matrix * assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); + render_model(m_cone.model, color, view_matrix * assemble_transform(offset, -0.5 * PI * Vec3d::UnitY(), cone_scale)); } - - shader->stop_using(); } void GLGizmoCut3D::render_cut_line() @@ -766,23 +759,10 @@ void GLGizmoCut3D::render_cut_line() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); - if (shader != nullptr) { - shader->start_using(); + m_cut_line.reset(); + m_cut_line.init_from(its_make_line((Vec3f)m_line_beg.cast(), (Vec3f)m_line_end.cast())); - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix()); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("width", 0.25f); - - m_cut_line.reset(); - m_cut_line.init_from(its_make_line((Vec3f)m_line_beg.cast(), (Vec3f)m_line_end.cast())); - - m_cut_line.set_color(GRABBER_COLOR); - m_cut_line.render(); - - shader->stop_using(); - } + render_line(m_cut_line, GRABBER_COLOR, wxGetApp().plater()->get_camera().get_view_matrix(), 0.25f); } bool GLGizmoCut3D::on_init() @@ -1303,7 +1283,7 @@ void GLGizmoCut3D::on_render() if (!m_hide_cut_plane && !m_connectors_editing) { render_cut_plane(); - render_cut_center_grabber(); + render_cut_plane_grabbers(); } render_cut_line(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index fe8422865..4335da35a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -221,7 +221,11 @@ private: void discard_cut_line_processing(); void render_cut_plane(); - void render_cut_center_grabber(); + void render_model(GLModel& model, const ColorRGBA& color, Transform3d view_model_matrix); + void render_line(GLModel& line_model, const ColorRGBA& color, Transform3d view_model_matrix, float width); + void render_rotation_snapping(Axis axis, const ColorRGBA& color); + void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); + void render_cut_plane_grabbers(); void render_cut_line(); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool force = false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index dbe0ba734..12c783c43 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -81,12 +81,7 @@ std::string GLGizmoMove3D::on_get_name() const bool GLGizmoMove3D::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - if (selection.is_any_volume() || selection.is_any_modifier()) { - if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) - return !m_parent.get_model()->objects[obj_idx]->is_cut(); - } - - return !selection.is_empty(); + return !selection.is_any_cut_volume() && !selection.is_any_connector() && !selection.is_empty(); } void GLGizmoMove3D::on_start_dragging() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 99d5eb881..80d6f1aa0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -869,12 +869,7 @@ std::string GLGizmoRotate3D::on_get_name() const bool GLGizmoRotate3D::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - if (selection.is_any_volume() || selection.is_any_modifier()) { - if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) - return !m_parent.get_model()->objects[obj_idx]->is_cut(); - } - - return !selection.is_empty(); + return !selection.is_any_cut_volume() && !selection.is_any_connector() && !selection.is_empty(); } void GLGizmoRotate3D::on_start_dragging() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 9adfa2241..9b49cf0b4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -168,12 +168,7 @@ std::string GLGizmoScale3D::on_get_name() const bool GLGizmoScale3D::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - if (selection.is_any_volume() || selection.is_any_modifier()) { - if (int obj_idx = selection.get_object_idx(); obj_idx >= 0) - return !m_parent.get_model()->objects[obj_idx]->is_cut(); - } - - return !selection.is_empty() && !selection.is_wipe_tower(); + return !selection.is_any_cut_volume() && !selection.is_any_connector() && !selection.is_empty() && !selection.is_wipe_tower(); } void GLGizmoScale3D::on_start_dragging() @@ -460,7 +455,7 @@ void GLGizmoScale3D::on_render() // draw grabbers render_grabbers(grabber_mean_size); } - else if (m_hover_id == 0 || m_hover_id == 1) { + else if ((m_hover_id == 0 || m_hover_id == 1) && m_grabbers[0].enabled && m_grabbers[1].enabled) { #if ENABLE_LEGACY_OPENGL_REMOVAL // draw connections #if ENABLE_GL_CORE_PROFILE @@ -505,7 +500,7 @@ void GLGizmoScale3D::on_render() shader->stop_using(); } } - else if (m_hover_id == 2 || m_hover_id == 3) { + else if ((m_hover_id == 2 || m_hover_id == 3) && m_grabbers[2].enabled && m_grabbers[3].enabled) { #if ENABLE_LEGACY_OPENGL_REMOVAL // draw connections #if ENABLE_GL_CORE_PROFILE @@ -550,7 +545,7 @@ void GLGizmoScale3D::on_render() shader->stop_using(); } } - else if (m_hover_id == 4 || m_hover_id == 5) { + else if ((m_hover_id == 4 || m_hover_id == 5) && m_grabbers[4].enabled && m_grabbers[5].enabled) { #if ENABLE_LEGACY_OPENGL_REMOVAL // draw connections #if ENABLE_GL_CORE_PROFILE diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index c6ddd1fa3..a3828f53d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -510,6 +510,28 @@ void Selection::volumes_changed(const std::vector &map_volume_old_to_new this->set_bounding_boxes_dirty(); } +bool Selection::is_any_connector() const +{ + const int obj_idx = get_object_idx(); + + if ((is_any_volume() || is_any_modifier() || is_mixed()) && // some solid_part AND/OR modifier is selected + obj_idx >= 0 && m_model->objects[obj_idx]->is_cut()) { + const ModelVolumePtrs& obj_volumes = m_model->objects[obj_idx]->volumes; + for (size_t vol_idx = 0; vol_idx < obj_volumes.size(); vol_idx++) + if (obj_volumes[vol_idx]->is_cut_connector()) + for (const GLVolume* v : *m_volumes) + if (v->object_idx() == obj_idx && v->volume_idx() == (int)vol_idx && v->selected) + return true; + } + return false; +} + +bool Selection::is_any_cut_volume() const +{ + const int obj_idx = get_object_idx(); + return is_any_volume() && obj_idx >= 0 && m_model->objects[obj_idx]->is_cut(); +} + bool Selection::is_single_full_instance() const { if (m_type == SingleFullInstance) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index cebea29e0..97a85a2dc 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -320,6 +320,8 @@ public: bool is_single_volume() const { return m_type == SingleVolume; } bool is_multiple_volume() const { return m_type == MultipleVolume; } bool is_any_volume() const { return is_single_volume() || is_multiple_volume(); } + bool is_any_connector() const; + bool is_any_cut_volume() const; bool is_mixed() const { return m_type == Mixed; } bool is_from_single_instance() const { return get_instance_idx() != -1; } bool is_from_single_object() const; From 58c7d8b1881127cafd971fa14dcf2fcc9ef9c5eb Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 3 Oct 2022 17:18:58 +0200 Subject: [PATCH 79/93] CutGizmo: Connectors mode: Implemented Rectangular selection of connectors + some code refactoring --- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 86 ++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 7 ++ src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 22 ++--- 6 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 09766bbac..43624964a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -210,7 +210,10 @@ public: #if ENABLE_RAYCAST_PICKING void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); } void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); } -#endif // ENABLE_RAYCAST_PICKING +#endif // ENABLE_RAYCAST_PICKING + + virtual bool is_in_editing_mode() const { return false; } + virtual bool is_selection_rectangle_dragging() const { return false; } protected: virtual bool on_init() = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 71a9c5dcf..775cd32cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -28,6 +28,7 @@ static const ColorRGBA HOVERED_DOWEL_COLOR = ColorRGBA(0.0f, 0.5f, 0.5f, 1.0f); static const ColorRGBA SELECTED_PLAG_COLOR = ColorRGBA::GRAY(); static const ColorRGBA SELECTED_DOWEL_COLOR = ColorRGBA::DARK_GRAY(); static const ColorRGBA CONNECTOR_DEF_COLOR = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f); +static const ColorRGBA CONNECTOR_ERR_COLOR = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); const unsigned int AngleResolution = 64; const unsigned int ScaleStepsCount = 72; @@ -1287,6 +1288,8 @@ void GLGizmoCut3D::on_render() } render_cut_line(); + + m_selection_rectangle.render(m_parent); } void GLGizmoCut3D::render_debug_input_window() @@ -1622,17 +1625,7 @@ void GLGizmoCut3D::render_connectors() m_selected.resize(connectors.size(), false); } - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - shader->start_using(); - - ScopeGuard guard([shader]() { shader->stop_using(); }); - - const Camera& camera = wxGetApp().plater()->get_camera(); - - ColorRGBA render_color; + ColorRGBA render_color = CONNECTOR_DEF_COLOR; const ModelInstance* mi = mo->instances[inst_id]; const Vec3d& instance_offset = mi->get_offset(); @@ -1656,11 +1649,11 @@ void GLGizmoCut3D::render_connectors() pos -= height * normal; height *= 2; } - pos[Z] += sla_shift; + pos += sla_shift * Vec3d::UnitZ(); // First decide about the color of the point. if (!m_connectors_editing) - render_color = CONNECTOR_DEF_COLOR; + render_color = CONNECTOR_ERR_COLOR; else if (size_t(m_hover_id - m_connectors_group_id) == i) render_color = connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR; else if (m_selected[i]) @@ -1678,22 +1671,19 @@ void GLGizmoCut3D::render_connectors() const Transform3d volume_trafo = get_volume_transformation(mv); if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { - render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : /*ColorRGBA(0.5f, 0.5f, 0.5f, 1.f)*/ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : CONNECTOR_ERR_COLOR; break; } - render_color = ColorRGBA(1.0f, 0.3f, 0.3f, 0.5f); + render_color = CONNECTOR_ERR_COLOR; m_has_invalid_connector = true; } } - m_shapes[connector.attribs].model.set_color(render_color); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * + scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); - const Transform3d scale_trafo = scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * scale_trafo; - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - m_shapes[connector.attribs].model.render(); + render_model(m_shapes[connector.attribs].model, render_color, view_model_matrix); } } @@ -1992,6 +1982,29 @@ bool GLGizmoCut3D::is_selection_changed(bool alt_down, bool control_down) return false; } +void GLGizmoCut3D::process_selection_rectangle(CutConnectors &connectors) +{ + GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + + ModelObject* mo = m_c->selection_info()->model_object(); + int active_inst = m_c->selection_info()->get_active_instance(); + + // First collect positions of all the points in world coordinates. + Transformation trafo = mo->instances[active_inst]->get_transformation(); + trafo.set_offset(trafo.get_offset() + double(m_c->selection_info()->get_sla_shift()) * Vec3d::UnitZ()); + + std::vector points; + for (const CutConnector&connector : connectors) + points.push_back(connector.pos + trafo.get_offset()); + + // Now ask the rectangle which of the points are inside. + std::vector points_idxs = m_selection_rectangle.contains(points); + m_selection_rectangle.stop_dragging(); + + for (size_t idx : points_idxs) + select_connector(int(idx), rectangle_status == GLSelectionRectangle::EState::Select); +} + bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { if (is_dragging() || m_connector_mode == CutConnectorMode::Auto || (!m_keep_upper || !m_keep_lower)) @@ -2001,20 +2014,43 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi (action == SLAGizmoEventType::LeftDown || action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::Moving) ) return process_cut_line(action, mouse_position); + if (!m_connectors_editing) + return false; + CutConnectors& connectors = m_c->selection_info()->model_object()->cut_connectors; - if (action == SLAGizmoEventType::LeftDown && !shift_down) { + if (action == SLAGizmoEventType::LeftDown) { + if (shift_down || alt_down) { + // left down with shift - show the selection rectangle: + if (m_hover_id == -1) + m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); + } + else // If there is no selection and no hovering, add new point if (m_hover_id == -1 && !control_down && !alt_down) if (!add_connector(connectors, mouse_position)) unselect_all_connectors(); return true; } - if (!m_connectors_editing) - return false; if (action == SLAGizmoEventType::LeftUp && !shift_down) return is_selection_changed(alt_down, control_down); + + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { + // Is this a selection or deselection rectangle? + process_selection_rectangle(connectors); + return true; + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging) { + if (m_selection_rectangle.is_dragging()) { + m_selection_rectangle.dragging(mouse_position); + return true; + } + return false; + } if (action == SLAGizmoEventType::RightDown && !shift_down) { // If any point is in hover state, this should initiate its move - return control back to GLCanvas: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 4335da35a..355c9230e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -2,6 +2,7 @@ #define slic3r_GLGizmoCut_hpp_ #include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLSelectionRectangle.hpp" #include "slic3r/GUI/GLModel.hpp" #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" @@ -95,6 +96,8 @@ class GLGizmoCut3D : public GLGizmoBase mutable std::vector m_selected; // which pins are currently selected int m_selected_count{ 0 }; + GLSelectionRectangle m_selection_rectangle; + bool m_has_invalid_connector{ false }; bool m_show_shortcuts{ false }; @@ -136,6 +139,9 @@ public: bool unproject_on_cut_plane(const Vec2d& mouse_pos, std::pair& pos_and_normal, Vec3d& pos_world); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool is_in_editing_mode() const override { return m_connectors_editing; } + bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); } + /// /// Drag of plane /// @@ -189,6 +195,7 @@ protected: bool delete_selected_connectors(CutConnectors&connectors); void select_connector(int idx, bool select); bool is_selection_changed(bool alt_down, bool control_down); + void process_selection_rectangle(CutConnectors &connectors); virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index aa8cdda04..0fdc3b2db 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -31,7 +31,7 @@ public: void data_changed() override; bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(); - bool is_selection_rectangle_dragging() const { + bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index ed48b6e5e..136276803 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -62,8 +62,8 @@ public: void delete_selected_points(bool force = false); //ClippingPlane get_sla_clipping_plane() const; - bool is_in_editing_mode() const { return m_editing_mode; } - bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } + bool is_in_editing_mode() const override { return m_editing_mode; } + bool is_selection_rectangle_dragging() const override { return m_selection_rectangle.is_dragging(); } bool has_backend_supports() const; void reslice_SLA_supports(bool postpone_error_messages = false) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 3946e54e1..add1eb94c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -610,20 +610,11 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) if (evt.GetEventType() == wxEVT_KEY_UP) { - if (m_current == SlaSupports || m_current == Hollow) + if (m_current == SlaSupports || m_current == Hollow || m_current == Cut) { - bool is_editing = true; - bool is_rectangle_dragging = false; - - if (m_current == SlaSupports) { - GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); - is_editing = gizmo->is_in_editing_mode(); - is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); - } - else { - GLGizmoHollow* gizmo = dynamic_cast(get_current()); - is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); - } + GLGizmoBase* gizmo = get_current(); + const bool is_editing = m_current == Hollow ? true : gizmo->is_in_editing_mode(); + const bool is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); if (keyCode == WXK_SHIFT) { @@ -645,7 +636,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) else if (evt.GetEventType() == wxEVT_KEY_DOWN) { if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) - && dynamic_cast(get_current())->is_in_editing_mode()) + && get_current()->is_in_editing_mode()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; @@ -662,6 +653,9 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) { case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; } case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } + case WXK_SHIFT : case WXK_ALT: { + processed = get_current()->is_in_editing_mode(); + } default: { break; } } } else if (m_current == Simplify && keyCode == WXK_ESCAPE) { From 74a32e3261f672e67457ec06944dddb27cf8f4b8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Oct 2022 10:03:55 +0200 Subject: [PATCH 80/93] Cut: Bug fixing and Improvements * CutGizmo: Fixed a label scale * Fixed deselection of selected connectors, when moving the camera * Implemented update of the settings for selected connectors * Connector selection: Ctrl shortcut is changed to Shift to compatibility of the selection/deselection with rectangle selection --- src/libslic3r/Model.hpp | 3 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 217 +++++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 17 ++- 3 files changed, 170 insertions(+), 67 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 01cd37414..0d54ebc1f 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -222,11 +222,13 @@ private: enum class CutConnectorType : int { Plug , Dowel + , Undef }; enum class CutConnectorStyle : int { Prizm , Frustum + , Undef //,Claw }; @@ -235,6 +237,7 @@ enum class CutConnectorShape : int { , Square , Hexagon , Circle + , Undef //,D-shape }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 775cd32cc..c90211d42 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -37,6 +37,9 @@ const unsigned int ScaleLongEvery = 2; const float ScaleLongTooth = 0.1f; // in percent of radius const unsigned int SnapRegionsCount = 8; +const float UndefFloat = -999.f; +const std::string UndefLabel = " "; + using namespace Geometry; // Generates mesh for a line @@ -274,8 +277,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) static bool pending_right_up = false; if (mouse_event.LeftDown()) { bool grabber_contains_mouse = (get_hover_id() != -1); - bool control_down = mouse_event.CmdDown(); - if ((!control_down || grabber_contains_mouse) && + const bool shift_down = mouse_event.ShiftDown(); + if ((!shift_down || grabber_contains_mouse) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) return true; } @@ -285,14 +288,14 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event) // don't allow dragging objects with the Sla gizmo on return true; } - else if (!control_down && + if (!control_down && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), false)) { // the gizmo got the event and took some action, no need to do // anything more here m_parent.set_as_dirty(); return true; } - else if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) { + if (control_down && (mouse_event.LeftIsDown() || mouse_event.RightIsDown())) { // CTRL has been pressed while already dragging -> stop current action if (mouse_event.LeftIsDown()) gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, mouse_event.ShiftDown(), mouse_event.AltDown(), true); @@ -427,7 +430,7 @@ bool GLGizmoCut3D::render_combo(const std::string& label, const std::vectortext(m_imperial_units ? _L("in") : _L("mm")); value_in = value * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); - return old_val != value; + return !is_approx(old_val, value); } -bool GLGizmoCut3D::render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in) +bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in) { ImGui::AlignTextToFramePadding(); m_imgui->text(label); ImGui::SameLine(m_label_width); ImGui::PushItemWidth(m_control_width * 0.85f); - float value = (float)value_in; + float value = value_in; if (m_imperial_units) value *= float(ObjectManipulation::mm_to_in); float old_val = value; + constexpr float UndefMinVal = -0.1f; + const BoundingBoxf3 bbox = bounding_box(); float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0); - float min_size = 1.f; + float min_size = value_in < 0.f ? UndefMinVal : 2.f; if (m_imperial_units) { mean_size *= float(ObjectManipulation::mm_to_in); min_size *= float(ObjectManipulation::mm_to_in); } - std::string format = m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); + std::string format = value_in < 0.f ? UndefLabel : + m_imperial_units ? "%.4f " + _u8L("in") : "%.2f " + _u8L("mm"); m_imgui->slider_float(("##" + label).c_str(), &value, min_size, mean_size, format.c_str()); - value_in = (double)(value) * (m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); + value_in = value * float(m_imperial_units ? ObjectManipulation::in_to_mm : 1.0); ImGui::SameLine(m_label_width + m_control_width + 3); ImGui::PushItemWidth(m_control_width * 0.3f); - float old_tolerance, tolerance = old_tolerance = (float)tolerance_in; - m_imgui->slider_float(("##tolerance_" + label).c_str(), &tolerance, 1.f, 20.f, "%.f %%", 1.f, true, _L("Tolerance")); - tolerance_in = (int)tolerance; + float old_tolerance, tolerance = old_tolerance = tolerance_in * 100.f; + std::string format_t = tolerance_in < 0.f ? UndefLabel : "%.f %%"; + float min_tolerance = tolerance_in < 0.f ? UndefMinVal : 0.f; - return old_val != value || old_tolerance != tolerance; + m_imgui->slider_float(("##tolerance_" + label).c_str(), &tolerance, min_tolerance, 20.f, format_t.c_str(), 1.f, true, _L("Tolerance")); + tolerance_in = tolerance * 0.01f; + + return !is_approx(old_val, value) || !is_approx(old_tolerance, tolerance); } void GLGizmoCut3D::render_move_center_input(int axis) @@ -772,15 +781,16 @@ bool GLGizmoCut3D::on_init() m_shortcut_key = WXK_CONTROL_C; // initiate info shortcuts - const wxString ctrl = GUI::shortkey_ctrl_prefix(); - const wxString alt = GUI::shortkey_alt_prefix(); + const wxString ctrl = GUI::shortkey_ctrl_prefix(); + const wxString alt = GUI::shortkey_alt_prefix(); + const wxString shift = "Shift+"; - m_shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add connector"))); - m_shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove connector"))); - m_shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move connector"))); - m_shortcuts.push_back(std::make_pair(ctrl + _L("Left click"), _L("Add connector to selection"))); - m_shortcuts.push_back(std::make_pair(alt + _L("Left click"), _L("Remove connector from selection"))); - m_shortcuts.push_back(std::make_pair(ctrl + "A", _L("Select all connectors"))); + m_shortcuts.push_back(std::make_pair(_L("Left click"), _L("Add connector"))); + m_shortcuts.push_back(std::make_pair(_L("Right click"), _L("Remove connector"))); + m_shortcuts.push_back(std::make_pair(_L("Drag"), _L("Move connector"))); + m_shortcuts.push_back(std::make_pair(shift + _L("Left click"), _L("Add connector to selection"))); + m_shortcuts.push_back(std::make_pair(alt + _L("Left click"), _L("Remove connector from selection"))); + m_shortcuts.push_back(std::make_pair(ctrl + "A", _L("Select all connectors"))); return true; } @@ -1214,7 +1224,7 @@ bool GLGizmoCut3D::update_bb() on_unregister_raycasters_for_picking(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { - m_selected.clear(); + clear_selection(); m_selected.resize(selection->model_object()->cut_connectors.size(), false); } @@ -1235,6 +1245,8 @@ void GLGizmoCut3D::init_picking_models() m_sphere.model.init_from(its); m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); } + if (m_shapes.empty()) + init_connector_shapes(); } void GLGizmoCut3D::init_rendering_items() @@ -1335,6 +1347,7 @@ void GLGizmoCut3D::unselect_all_connectors() { std::fill(m_selected.begin(), m_selected.end(), false); m_selected_count = 0; + validate_connector_settings(); } void GLGizmoCut3D::select_all_connectors() @@ -1361,6 +1374,8 @@ void GLGizmoCut3D::apply_selected_connectors(std::function app for (size_t idx = 0; idx < m_selected.size(); idx++) if (m_selected[idx]) apply_fn(idx); + + update_raycasters_for_picking_transform(); } void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) @@ -1399,15 +1414,19 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance)) - apply_selected_connectors([this, &connectors](size_t idx) { - connectors[idx].height = float(m_connector_depth_ratio); - connectors[idx].height_tolerance = 0.01f * float(m_connector_depth_ratio_tolerance); + apply_selected_connectors([this, &connectors](size_t idx) { + if (m_connector_depth_ratio > 0) + connectors[idx].height = m_connector_depth_ratio; + if (m_connector_depth_ratio_tolerance >= 0) + connectors[idx].height_tolerance = m_connector_depth_ratio_tolerance; }); if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance)) - apply_selected_connectors([this, &connectors](size_t idx) { - connectors[idx].radius = float(m_connector_size * 0.5); - connectors[idx].radius_tolerance = 0.01f * float(m_connector_size_tolerance); + apply_selected_connectors([this, &connectors](size_t idx) { + if (m_connector_size > 0) + connectors[idx].radius = 0.5f * m_connector_size; + if (m_connector_size_tolerance >= 0) + connectors[idx].radius_tolerance = m_connector_size_tolerance; }); ImGui::Separator(); @@ -1529,26 +1548,84 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) m_imgui->disabled_end(); } +void GLGizmoCut3D::validate_connector_settings() +{ + if (m_connector_depth_ratio < 0.f) + m_connector_depth_ratio = 3.f; + if (m_connector_depth_ratio_tolerance < 0.f) + m_connector_depth_ratio_tolerance = 0.1f; + if (m_connector_size < 0.f) + m_connector_size = 2.5f; + if (m_connector_size_tolerance < 0.f) + m_connector_size_tolerance = 0.f; + + if (m_connector_type == CutConnectorType::Undef) + m_connector_type = CutConnectorType::Plug; + if (m_connector_style == size_t(CutConnectorStyle::Undef)) + m_connector_style = size_t(CutConnectorStyle::Prizm); + if (m_connector_shape_id == size_t(CutConnectorShape::Undef)) + m_connector_shape_id = size_t(CutConnectorShape::Circle); +} + void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) { m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - m_label_width = m_imgui->get_style_scaling() * 100.0f; - m_control_width = m_imgui->get_style_scaling() * 150.0f; + m_label_width = m_imgui->get_font_size() * 6.f; + m_control_width = m_imgui->get_font_size() * 9.f; - if (m_selected_count == 1) + if (m_connectors_editing && m_selected_count > 0) { + float depth_ratio { UndefFloat }; + float depth_ratio_tolerance { UndefFloat }; + float radius { UndefFloat }; + float radius_tolerance { UndefFloat }; + CutConnectorType type { CutConnectorType::Undef }; + CutConnectorStyle style { CutConnectorStyle::Undef }; + CutConnectorShape shape { CutConnectorShape::Undef }; + + bool is_init = false; for (size_t idx = 0; idx < m_selected.size(); idx++) if (m_selected[idx]) { - auto&connector = connectors[idx]; - m_connector_depth_ratio = connector.height; - m_connector_depth_ratio_tolerance = 100 * connector.height_tolerance; - m_connector_size = 2. * connector.radius; - m_connector_size_tolerance = 100 * connector.radius_tolerance; - m_connector_type = connector.attribs.type; - m_connector_style = size_t(connector.attribs.style); - m_connector_shape_id = size_t(connector.attribs.shape); + const CutConnector& connector = connectors[idx]; + if (!is_init) { + depth_ratio = connector.height; + depth_ratio_tolerance = connector.height_tolerance; + radius = connector.radius; + radius_tolerance = connector.radius_tolerance; + type = connector.attribs.type; + style = connector.attribs.style; + shape = connector.attribs.shape; - break; + if (m_selected_count == 1) + break; + is_init = true; + } + else { + if (!is_approx(depth_ratio, connector.height)) + depth_ratio = UndefFloat; + if (!is_approx(depth_ratio_tolerance, connector.height_tolerance)) + depth_ratio_tolerance = UndefFloat; + if (!is_approx(radius,connector.radius)) + radius = UndefFloat; + if (!is_approx(radius_tolerance, connector.radius_tolerance)) + radius_tolerance = UndefFloat; + + if (type != connector.attribs.type) + type = CutConnectorType::Undef; + if (style != connector.attribs.style) + style = CutConnectorStyle::Undef; + if (shape != connector.attribs.shape) + shape = CutConnectorShape::Undef; + } } + + m_connector_depth_ratio = depth_ratio; + m_connector_depth_ratio_tolerance = depth_ratio_tolerance; + m_connector_size = 2.f * radius; + m_connector_size_tolerance = radius_tolerance; + m_connector_type = type; + m_connector_style = size_t(style); + m_connector_shape_id = size_t(shape); + } } void GLGizmoCut3D::render_input_window_warning() const @@ -1621,7 +1698,7 @@ void GLGizmoCut3D::render_connectors() const CutConnectors& connectors = mo->cut_connectors; if (connectors.size() != m_selected.size()) { // #ysFIXME - m_selected.clear(); + clear_selection(); m_selected.resize(connectors.size(), false); } @@ -1699,7 +1776,7 @@ bool GLGizmoCut3D::can_perform_cut() const void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object) { if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { - m_selected.clear(); + clear_selection(); for (CutConnector&connector : mo->cut_connectors) { connector.rotation_m = m_rotation_m; @@ -1810,21 +1887,34 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair return false; } +void GLGizmoCut3D::clear_selection() +{ + m_selected.clear(); + m_selected_count = 0; +} + void GLGizmoCut3D::reset_connectors() { m_c->selection_info()->model_object()->cut_connectors.clear(); update_model_object(); - m_selected.clear(); + clear_selection(); +} + +void GLGizmoCut3D::init_connector_shapes() +{ + for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug}) + for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prizm}) + for (const CutConnectorShape& shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) { + const CutConnectorAttributes attribs = { type, style, shape }; + const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); + m_shapes[attribs].model.init_from(its); + m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); + } } void GLGizmoCut3D::update_connector_shape() { CutConnectorAttributes attribs = { m_connector_type, CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id) }; - if (m_shapes.find(attribs) == m_shapes.end()) { - const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); - m_shapes[attribs].model.init_from(its); - m_shapes[attribs].mesh_raycaster = std::make_unique(std::make_shared(std::move(its)));; - } const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs); m_connector_mesh.clear(); @@ -1913,15 +2003,16 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p if (m_connectors_editing) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); + unselect_all_connectors(); connectors.emplace_back(hit, m_rotation_m, - float(m_connector_size) * 0.5f, float(m_connector_depth_ratio), - float(m_connector_size_tolerance) * 0.01f, float(m_connector_depth_ratio_tolerance) * 0.01f, + m_connector_size * 0.5f, m_connector_depth_ratio, + m_connector_size_tolerance, m_connector_depth_ratio_tolerance, CutConnectorAttributes( CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); - unselect_all_connectors(); m_selected.push_back(true); + m_selected_count = 1; assert(m_selected.size() == connectors.size()); update_model_object(); m_parent.set_as_dirty(); @@ -1951,6 +2042,7 @@ bool GLGizmoCut3D::delete_selected_connectors(CutConnectors& connectors) // remove selections m_selected.erase(std::remove_if(m_selected.begin(), m_selected.end(), [](const auto& selected) { return selected; }), m_selected.end()); + m_selected_count = 0; assert(m_selected.size() == connectors.size()); update_model_object(); @@ -1967,13 +2059,13 @@ void GLGizmoCut3D::select_connector(int idx, bool select) --m_selected_count; } -bool GLGizmoCut3D::is_selection_changed(bool alt_down, bool control_down) +bool GLGizmoCut3D::is_selection_changed(bool alt_down, bool shift_down) { if (m_hover_id >= m_connectors_group_id) { if (alt_down) select_connector(m_hover_id - m_connectors_group_id, false); else { - if (!control_down) + if (!shift_down) unselect_all_connectors(); select_connector(m_hover_id - m_connectors_group_id, true); } @@ -2026,15 +2118,18 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect); } else - // If there is no selection and no hovering, add new point - if (m_hover_id == -1 && !control_down && !alt_down) - if (!add_connector(connectors, mouse_position)) - unselect_all_connectors(); + // If there is no selection and no hovering, add new point + if (m_hover_id == -1 && !shift_down && !alt_down) + if (!add_connector(connectors, mouse_position)) + m_ldown_mouse_position = mouse_position; return true; } - if (action == SLAGizmoEventType::LeftUp && !shift_down) - return is_selection_changed(alt_down, control_down); + if (action == SLAGizmoEventType::LeftUp) { + if ((m_ldown_mouse_position - mouse_position).norm() < 5.) + unselect_all_connectors(); + return is_selection_changed(alt_down, shift_down); + } // left up with selection rectangle - select points inside the rectangle: if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 355c9230e..c3916e4a8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -55,6 +55,8 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_line_beg{ Vec3d::Zero() }; Vec3d m_line_end{ Vec3d::Zero() }; + Vec2d m_ldown_mouse_position{ Vec2d::Zero() }; + GLModel m_plane; GLModel m_grabber_connection; GLModel m_cut_line; @@ -82,11 +84,11 @@ class GLGizmoCut3D : public GLGizmoBase bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; - double m_connector_depth_ratio{ 3.0 }; - double m_connector_size{ 2.5 }; + float m_connector_depth_ratio{ 3.f }; + float m_connector_size{ 2.5f }; - int m_connector_depth_ratio_tolerance{ 10 }; - int m_connector_size_tolerance{ 0 }; + float m_connector_depth_ratio_tolerance{ 0.1f }; + float m_connector_size_tolerance{ 0.f }; float m_label_width{ 150.0 }; float m_control_width{ 200.0 }; @@ -194,7 +196,7 @@ protected: bool add_connector(CutConnectors&connectors, const Vec2d&mouse_position); bool delete_selected_connectors(CutConnectors&connectors); void select_connector(int idx, bool select); - bool is_selection_changed(bool alt_down, bool control_down); + bool is_selection_changed(bool alt_down, bool shift_down); void process_selection_rectangle(CutConnectors &connectors); virtual void on_register_raycasters_for_picking() override; @@ -214,7 +216,7 @@ private: void set_center(const Vec3d& center); bool render_combo(const std::string& label, const std::vector& lines, size_t& selection_idx); bool render_double_input(const std::string& label, double& value_in); - bool render_slider_double_input(const std::string& label, double& value_in, int& tolerance_in); + bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in); void render_move_center_input(int axis); void render_connect_mode_radio_button(CutConnectorMode mode); bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; @@ -240,8 +242,11 @@ private: void init_picking_models(); void init_rendering_items(); void render_clipper_cut(); + void clear_selection(); void reset_connectors(); + void init_connector_shapes(); void update_connector_shape(); + void validate_connector_settings(); void update_model_object(); bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); }; From 85af9b93f1e886ea0f80d890d97ac967da772cb9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Oct 2022 16:26:19 +0200 Subject: [PATCH 81/93] Cut: Fixes and improvements for object's context menu * Disable or delete some menu items, which are inappropriate for cut objects * For cut objects added menu item "Invalidate cut info" to disconnect related cut parts of initial object * If just one part is kept after cut performance, than don't apply a cut info for this object. + CutGizmo: Fixed selection of the mode An object has connectors -> Connectors mode An object doesn't has connectors -> CutPlane mode --- src/libslic3r/Model.cpp | 8 +++++- src/libslic3r/ObjectID.hpp | 2 +- src/slic3r/GUI/GUI_Factories.cpp | 22 +++++++++++++++++ src/slic3r/GUI/GUI_Factories.hpp | 1 + src/slic3r/GUI/GUI_ObjectList.cpp | 34 +++++++++++++++++++++++++- src/slic3r/GUI/GUI_ObjectList.hpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 13 +++++----- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 1 + src/slic3r/GUI/Plater.cpp | 18 +++++++------- 10 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 270ccb1be..597152c06 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1261,7 +1261,9 @@ void ModelObject::invalidate_cut() { for (ModelObject* obj : m_model->objects) if (obj != this && obj->cut_id.is_equal(this->cut_id)) - obj->cut_id.ivalidate(); + obj->cut_id.invalidate(); + // invalidate own cut_id + this->cut_id.invalidate(); } void ModelObject::synchronize_model_after_cut() @@ -1276,6 +1278,10 @@ void ModelObject::synchronize_model_after_cut() void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes) { + // we don't save cut information, if result will not contains all parts of initial object + if (!attributes.has(ModelObjectCutAttribute::KeepUpper) || !attributes.has(ModelObjectCutAttribute::KeepLower)) + return; + if (cut_id.id().invalid()) cut_id.init(); { diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 452f620c4..f907b03e1 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -161,7 +161,7 @@ public: return *this; } - void ivalidate() { + void invalidate() { set_invalid_id(); m_check_sum = 1; m_connectors_cnt = 0; diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 3cfc55adc..5164c691e 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -685,6 +685,24 @@ wxMenuItem* MenuFactory::append_menu_item_printable(wxMenu* menu) return menu_item_printable; } +void MenuFactory::append_menu_item_invalidate_cut_info(wxMenu* menu) +{ + const wxString menu_name = _L("Invalidate cut info"); + + bool is_cut = obj_list()->has_selected_cut_object(); + + auto menu_item_id = menu->FindItem(menu_name); + if (menu_item_id != wxNOT_FOUND) { + // Delete old menu item if selected object isn't cut + if (!is_cut) + menu->Destroy(menu_item_id); + } + else if (is_cut) + append_menu_item(menu, wxID_ANY, menu_name, "", + [](wxCommandEvent&) { obj_list()->invalidate_cut_info_for_selection(); }, "", menu, + []() { return true; }, m_parent); +} + void MenuFactory::append_menu_items_osx(wxMenu* menu) { append_menu_item(menu, wxID_ANY, _L("Rename"), "", @@ -821,6 +839,8 @@ void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* ModelObjectPtrs objects; for (int obj_idx : obj_idxs) { ModelObject* object = obj_list()->object(obj_idx); + if (object->is_cut()) + return false; if (vol_idxs.empty()) { for (ModelVolume* volume : object->volumes) if (volume_respects_conversion(volume, conver_type)) @@ -1021,6 +1041,7 @@ wxMenu* MenuFactory::object_menu() append_menu_item_settings(&m_object_menu); append_menu_item_change_extruder(&m_object_menu); update_menu_items_instance_manipulation(mtObjectFFF); + append_menu_item_invalidate_cut_info(&m_object_menu); return &m_object_menu; } @@ -1030,6 +1051,7 @@ wxMenu* MenuFactory::sla_object_menu() append_menu_items_convert_unit(&m_sla_object_menu, 11); append_menu_item_settings(&m_sla_object_menu); update_menu_items_instance_manipulation(mtObjectSLA); + append_menu_item_invalidate_cut_info(&m_sla_object_menu); return &m_sla_object_menu; } diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index bbbc00d42..7190edb64 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -89,6 +89,7 @@ private: wxMenuItem* append_menu_item_change_type(wxMenu* menu); wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu); wxMenuItem* append_menu_item_printable(wxMenu* menu); + void append_menu_item_invalidate_cut_info(wxMenu *menu); void append_menu_items_osx(wxMenu* menu); wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu); wxMenuItem* append_menu_item_simplify(wxMenu* menu); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d3f1d1bf2..cced147f8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2443,9 +2443,41 @@ bool ObjectList::can_split_instances() return selection.is_multiple_full_instance() || selection.is_single_full_instance(); } +bool ObjectList::has_selected_cut_object() const +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.IsEmpty()) + return false; + + for (wxDataViewItem item : sels) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0 && object(obj_idx)->is_cut()) + return true; + } + + return false; +} + +void ObjectList::invalidate_cut_info_for_selection() +{ + wxDataViewItemArray sels; + GetSelections(sels); + if (sels.IsEmpty()) + return; + + for (wxDataViewItem item : sels) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0 && object(obj_idx)->is_cut()) + object(obj_idx)->invalidate_cut(); + } + + update_lock_icons_for_model(); +} + bool ObjectList::can_merge_to_multipart_object() const { - if (printer_technology() == ptSLA) + if (printer_technology() == ptSLA || has_selected_cut_object()) return false; wxDataViewItemArray sels; diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index cb498f87e..3dd02ab8d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -277,6 +277,8 @@ public: bool is_splittable(bool to_objects); bool selected_instances_of_same_object(); bool can_split_instances(); + bool has_selected_cut_object() const; + void invalidate_cut_info_for_selection(); bool can_merge_to_multipart_object() const; bool can_merge_to_single_object() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index c90211d42..d75132542 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1226,6 +1226,7 @@ bool GLGizmoCut3D::update_bb() if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { clear_selection(); m_selected.resize(selection->model_object()->cut_connectors.size(), false); + m_connectors_editing = !m_selected.empty(); } return true; @@ -1333,12 +1334,12 @@ void GLGizmoCut3D::adjust_window_position(float x, float y, float bottom_limit) ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); - if (last_h != win_h || last_y != y) { + if (!is_approx(last_h, win_h) || !is_approx(last_y, y)) { // ask canvas for another frame to render the window in the correct position m_imgui->set_requires_extra_frame(); - if (last_h != win_h) + if (!is_approx(last_h, win_h)) last_h = win_h; - if (last_y != y) + if (!is_approx(last_y, y)) last_y = y; } } @@ -1773,9 +1774,9 @@ bool GLGizmoCut3D::can_perform_cut() const return tbb.contains(m_plane_center); } -void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object) +void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowels_as_separate_object) { - if (has_connectors && m_connector_mode == CutConnectorMode::Manual) { + if (m_connector_mode == CutConnectorMode::Manual) { clear_selection(); for (CutConnector&connector : mo->cut_connectors) { @@ -1824,7 +1825,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); // update connectors pos as offset of its center before cut performing - apply_connectors_in_model(mo, has_connectors, create_dowels_as_separate_object); + apply_connectors_in_model(mo, create_dowels_as_separate_object); } plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index c3916e4a8..21d7c64f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -225,7 +225,7 @@ private: void render_connectors(); bool can_perform_cut() const; - void apply_connectors_in_model(ModelObject* mo, const bool has_connectors, bool &create_dowels_as_separate_object); + void apply_connectors_in_model(ModelObject* mo, bool &create_dowels_as_separate_object); bool cut_line_processing() const; void discard_cut_line_processing(); diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 0475fe395..e2fca5fae 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1807,6 +1807,7 @@ void ObjectDataViewModel::UpdateLockIcon(const wxDataViewItem& item, bool has_lo for (const wxDataViewItem& child : children) UpdateLockIcon(child, has_lock); } + ItemChanged(item); } } // namespace GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 12a654175..7c7fbc989 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4786,7 +4786,8 @@ bool Plater::priv::can_split(bool to_objects) const bool Plater::priv::can_scale_to_print_volume() const { const BuildVolume::Type type = this->bed.build_volume().type(); - return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle); + return !sidebar->obj_list()->has_selected_cut_object() && + !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle); } bool Plater::priv::layers_height_allowed() const @@ -4801,16 +4802,19 @@ bool Plater::priv::layers_height_allowed() const bool Plater::priv::can_mirror() const { - return get_selection().is_from_single_instance(); + return !sidebar->obj_list()->has_selected_cut_object() && get_selection().is_from_single_instance(); } bool Plater::priv::can_replace_with_stl() const { - return get_selection().get_volume_idxs().size() == 1; + return !sidebar->obj_list()->has_selected_cut_object() && get_selection().get_volume_idxs().size() == 1; } bool Plater::priv::can_reload_from_disk() const { + if (sidebar->obj_list()->has_selected_cut_object()) + return false; + #if ENABLE_RELOAD_FROM_DISK_REWORK // collect selected reloadable ModelVolumes std::vector> selected_volumes = reloadable_volumes(model, get_selection()); @@ -4939,9 +4943,7 @@ bool Plater::priv::can_increase_instances() const || q->canvas3D()->get_gizmos_manager().is_in_editing_mode()) return false; - int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) - && !model.objects[obj_idx]->is_cut(); + return !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_decrease_instances() const @@ -4950,9 +4952,7 @@ bool Plater::priv::can_decrease_instances() const || q->canvas3D()->get_gizmos_manager().is_in_editing_mode()) return false; - int obj_idx = get_selected_object_idx(); - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1) - && !model.objects[obj_idx]->is_cut(); + return !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_split_to_objects() const From 64c57faf8faf0a5b9375f75f36db07a5834ebb30 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Oct 2022 10:46:33 +0200 Subject: [PATCH 82/93] Cut Improvements: Fixed Undo/Redo for cut performance + ObjectList: Fixed items update after Invalidate cut information + CutGizmo: Fixed wrong mode selection after delete object and that add new --- src/libslic3r/Model.cpp | 6 ++-- src/libslic3r/Model.hpp | 18 ++++++++---- src/libslic3r/ObjectID.hpp | 9 +++++- src/slic3r/GUI/GUI_ObjectList.cpp | 42 ++++++++++++++++++++-------- src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 19 +++++++------ src/slic3r/GUI/Plater.cpp | 11 ++++---- 7 files changed, 69 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 597152c06..54d4fcab7 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1259,11 +1259,9 @@ void ModelObject::apply_cut_connectors(const std::string& new_name) void ModelObject::invalidate_cut() { - for (ModelObject* obj : m_model->objects) - if (obj != this && obj->cut_id.is_equal(this->cut_id)) - obj->cut_id.invalidate(); - // invalidate own cut_id this->cut_id.invalidate(); + for (ModelVolume* volume : this->volumes) + volume->invalidate_cut_info(); } void ModelObject::synchronize_model_after_cut() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0d54ebc1f..ee611b006 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -444,7 +444,7 @@ public: size_t parts_count() const; static indexed_triangle_set get_connector_mesh(CutConnectorAttributes connector_attributes); void apply_cut_connectors(const std::string& name); - // invalidate cut state for this and related objects from the whole model + // invalidate cut state for this object and its connectors/volumes void invalidate_cut(); void synchronize_model_after_cut(); void apply_cut_attributes(ModelObjectCutAttributes attributes); @@ -742,10 +742,16 @@ public: {} void set_processed() { is_processed = true; } + void invalidate() { is_connector = false; } + + template inline void serialize(Archive& ar) { + ar(is_connector, is_processed, connector_type, radius_tolerance, height_tolerance); + } }; CutInfo cut_info; bool is_cut_connector() const { return cut_info.is_processed && cut_info.is_connector; } + void invalidate_cut_info() { cut_info.invalidate(); } // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } @@ -954,7 +960,8 @@ private: ObjectBase(other), name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation), - supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets) + supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets), + cut_info(other.cut_info) { assert(this->id().valid()); assert(this->config.id().valid()); @@ -974,7 +981,8 @@ private: } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) : - name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation) + name(other.name), source(other.source), config(other.config), object(object), m_mesh(new TriangleMesh(std::move(mesh))), m_type(other.m_type), m_transformation(other.m_transformation), + cut_info(other.cut_info) { assert(this->id().valid()); assert(this->config.id().valid()); @@ -1016,7 +1024,7 @@ private: } template void load(Archive &ar) { bool has_convex_hull; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, cut_info); cereal::load_by_value(ar, supported_facets); cereal::load_by_value(ar, seam_facets); cereal::load_by_value(ar, mmu_segmentation_facets); @@ -1032,7 +1040,7 @@ private: } template void save(Archive &ar) const { bool has_convex_hull = m_convex_hull.get() != nullptr; - ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull); + ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, cut_info); cereal::save_by_value(ar, supported_facets); cereal::save_by_value(ar, seam_facets); cereal::save_by_value(ar, mmu_segmentation_facets); diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index f907b03e1..6be19c88f 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -156,7 +156,7 @@ public: this->m_check_sum = rhs.check_sum(); this->m_connectors_cnt = rhs.connectors_cnt() ; } - CutObjectBase operator=(const CutObjectBase& other) { + CutObjectBase& operator=(const CutObjectBase& other) { this->copy(other); return *this; } @@ -179,6 +179,13 @@ public: size_t connectors_cnt() const { return m_connectors_cnt; } void increase_connectors_cnt(size_t connectors_cnt) { m_connectors_cnt += connectors_cnt; } + +private: + friend class cereal::access; + template void serialize(Archive& ar) { + ar(cereal::base_class(this)); + ar(m_check_sum, m_connectors_cnt); + } }; // Unique object / instance ID for the wipe tower. diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cced147f8..663290a8b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2458,19 +2458,37 @@ bool ObjectList::has_selected_cut_object() const return false; } - void ObjectList::invalidate_cut_info_for_selection() { - wxDataViewItemArray sels; - GetSelections(sels); - if (sels.IsEmpty()) + const wxDataViewItem item = GetSelection(); + if (item) { + const int obj_idx = m_objects_model->GetObjectIdByItem(item); + if (obj_idx >= 0) + invalidate_cut_info_for_object(size_t(obj_idx)); + } +} + +void ObjectList::invalidate_cut_info_for_object(size_t obj_idx) +{ + ModelObject* init_obj = object(int(obj_idx)); + if (!init_obj->is_cut()) return; - for (wxDataViewItem item : sels) { - const int obj_idx = m_objects_model->GetObjectIdByItem(item); - if (obj_idx >= 0 && object(obj_idx)->is_cut()) - object(obj_idx)->invalidate_cut(); - } + take_snapshot(_L("Invalidate cut info")); + + auto invalidate_cut = [this](size_t obj_idx) { + object(int(obj_idx))->invalidate_cut(); + update_info_items(obj_idx); + add_volumes_to_object_in_list(obj_idx); + }; + + // invalidate cut for related objects (which have the same cut_id) + for (size_t idx = 0; idx < m_objects->size(); idx++) + if (ModelObject* obj = object(idx); obj != init_obj && obj->cut_id.is_equal(init_obj->cut_id)) + invalidate_cut(idx); + + // invalidate own cut information + invalidate_cut(size_t(obj_idx)); update_lock_icons_for_model(); } @@ -2970,8 +2988,6 @@ void ObjectList::update_lock_icons_for_model() bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx) { -// take_snapshot(_(L("Delete Selected Item"))); // #ysFIXME - delete this redundant snapshot after test - if (type & (itObject | itVolume | itInstance)) { if (type & itObject) { bool was_cut = object(obj_idx)->is_cut(); @@ -2983,7 +2999,7 @@ bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i } return false; } - else if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) { + if (del_subobject_from_object(obj_idx, sub_obj_idx, type)) { type == itVolume ? delete_volume_from_list(obj_idx, sub_obj_idx) : delete_instance_from_list(obj_idx, sub_obj_idx); return true; @@ -3189,6 +3205,8 @@ void ObjectList::remove() if (m_objects_model->InvalidItem(item)) // item can be deleted for this moment (like last 2 Instances or Volumes) continue; parent = delete_item(item); + if (parent == item && m_objects_model->GetItemType(item) & itObject) // Object wasn't deleted + break; } } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 3dd02ab8d..eb718b48d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -279,6 +279,7 @@ public: bool can_split_instances(); bool has_selected_cut_object() const; void invalidate_cut_info_for_selection(); + void invalidate_cut_info_for_object(size_t obj_idx); bool can_merge_to_multipart_object() const; bool can_merge_to_single_object() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d75132542..459c6390c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1223,11 +1223,10 @@ bool GLGizmoCut3D::update_bb() on_unregister_raycasters_for_picking(); - if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) { - clear_selection(); + clear_selection(); + if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) m_selected.resize(selection->model_object()->cut_connectors.size(), false); - m_connectors_editing = !m_selected.empty(); - } + m_connectors_editing = !m_selected.empty(); return true; } @@ -1811,6 +1810,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) if (!mo) return; + // deactivate CutGizmo and than perform a cut + m_parent.reset_all_gizmos(); + // m_cut_z is the distance from the bed. Subtract possible SLA elevation. const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); const double object_cut_z = m_plane_center.z() - sla_shift_z; @@ -1820,13 +1822,12 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) cut_center_offset[Z] -= sla_shift_z; if (0.0 < object_cut_z && can_perform_cut()) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); + bool create_dowels_as_separate_object = false; const bool has_connectors = !mo->cut_connectors.empty(); - { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); - // update connectors pos as offset of its center before cut performing - apply_connectors_in_model(mo, create_dowels_as_separate_object); - } + // update connectors pos as offset of its center before cut performing + apply_connectors_in_model(mo, create_dowels_as_separate_object); plater->cut(object_idx, instance_idx, assemble_transform(cut_center_offset) * m_rotation_m, only_if(has_connectors ? true : m_keep_upper, ModelObjectCutAttribute::KeepUpper) | diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7c7fbc989..2051192ee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3020,8 +3020,6 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) dialog.SetButtonLabel(wxID_YES, _L("Delete object")); if (dialog.ShowModal() == wxID_CANCEL) return false; - // unmark all related CutParts of initial object - obj->invalidate_cut(); } wxString snapshot_label = _L("Delete Object"); @@ -3029,6 +3027,10 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx) snapshot_label += ": " + wxString::FromUTF8(obj->name.c_str()); Plater::TakeSnapshot snapshot(q, snapshot_label); m_worker.cancel_all(); + + if (obj->is_cut()) + sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx); + model.delete_object(obj_idx); update(); object_list_changed(); @@ -5940,9 +5942,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds"); - this->suppress_snapshots(); wxBusyCursor wait; - + const auto new_objects = object->cut(instance_idx, cut_matrix, attributes); model().delete_object(obj_idx); @@ -5951,8 +5952,6 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, const Transform3d& cut_mat // suppress to call selection update for Object List to avoid call of early Gizmos on/off update p->load_model_objects(new_objects, false, false); - this->allow_snapshots(); - // now process all updates of the 3d scene update(); // Update InfoItems in ObjectList after update() to use of a correct value of the GLCanvas3D::is_sinking(), From 13e4e85e3d2b53382915909c46b2340abd88aac1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Oct 2022 15:23:31 +0200 Subject: [PATCH 83/93] Cut bug fixing: Model: fixed looks_like_imperial_units(). This function respects to cut status now. To be detected as looks_like_imperial_units, all parts of cat object have to be looks_like_imperial_units(). ObjectList: Fixed update after adding/deleting of the modifiers for cut object GUI_Factories: Fixed a place of the "Invalidate cut info" item in object menu --- src/libslic3r/Model.cpp | 19 ++++++++++++++++--- src/slic3r/GUI/GUI_Factories.cpp | 11 ++++------- src/slic3r/GUI/GUI_ObjectList.cpp | 17 +++++++++++++---- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 54d4fcab7..56f3af2d1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -464,12 +464,25 @@ static constexpr const double volume_threshold_inches = 9.0; // 9 = 3*3*3; bool Model::looks_like_imperial_units() const { - if (this->objects.size() == 0) + if (this->objects.empty()) return false; for (ModelObject* obj : this->objects) - if (obj->get_object_stl_stats().volume < volume_threshold_inches) - return true; + if (obj->get_object_stl_stats().volume < volume_threshold_inches) { + if (!obj->is_cut()) + return true; + bool all_cut_parts_look_like_imperial_units = true; + for (ModelObject* obj_other : this->objects) { + if (obj_other == obj) + continue; + if (obj_other->cut_id.is_equal(obj->cut_id) && obj_other->get_object_stl_stats().volume >= volume_threshold_inches) { + all_cut_parts_look_like_imperial_units = false; + break; + } + } + if (all_cut_parts_look_like_imperial_units) + return true; + } return false; } diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 5164c691e..ff9113c68 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -689,15 +689,12 @@ void MenuFactory::append_menu_item_invalidate_cut_info(wxMenu* menu) { const wxString menu_name = _L("Invalidate cut info"); - bool is_cut = obj_list()->has_selected_cut_object(); - auto menu_item_id = menu->FindItem(menu_name); - if (menu_item_id != wxNOT_FOUND) { + if (menu_item_id != wxNOT_FOUND) // Delete old menu item if selected object isn't cut - if (!is_cut) - menu->Destroy(menu_item_id); - } - else if (is_cut) + menu->Destroy(menu_item_id); + + if (obj_list()->has_selected_cut_object()) append_menu_item(menu, wxID_ANY, menu_name, "", [](wxCommandEvent&) { obj_list()->invalidate_cut_info_for_selection(); }, "", menu, []() { return true; }, m_parent); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 663290a8b..d9cc1b1ca 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1728,6 +1728,9 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode // update printable state on canvas wxGetApp().plater()->canvas3D()->update_instance_printable_state_for_object((size_t)obj_idx); + if (model_object.is_cut()) + update_info_items(obj_idx); + selection_changed(); } @@ -1858,11 +1861,13 @@ bool ObjectList::del_subobject_item(wxDataViewItem& item) // If last volume item with warning was deleted, unmark object item if (type & itVolume) { + add_volumes_to_object_in_list(obj_idx); const std::string& icon_name = get_warning_icon_name(object(obj_idx)->get_object_stl_stats()); m_objects_model->UpdateWarningIcon(parent, icon_name); } + else + m_objects_model->Delete(item); - m_objects_model->Delete(item); update_info_items(obj_idx); return true; @@ -2677,7 +2682,8 @@ void ObjectList::part_selection_changed() volume_id = m_objects_model->GetVolumeIdByItem(item); m_config = &object->volumes[volume_id]->config; update_and_show_manipulations = true; - enable_manipulation = !(object->is_cut() && object->volumes[volume_id]->is_cut_connector()); + const ModelVolume* volume = object->volumes[volume_id]; + enable_manipulation = !(object->is_cut() && (volume->is_cut_connector() || volume->is_model_part())); } else if (type & itInstance) { og_name = _L("Instance manipulation"); @@ -2874,8 +2880,11 @@ static bool can_add_volumes_to_object(const ModelObject* object) if (can && object->is_cut()) { int no_connectors_cnt = 0; for (const ModelVolume* v : object->volumes) - if (!v->is_cut_connector()) + if (!v->is_cut_connector()) { + if (!v->is_model_part()) + return true; no_connectors_cnt++; + } can = no_connectors_cnt > 1; } @@ -3031,7 +3040,7 @@ bool ObjectList::delete_from_model_and_list(const std::vector& it if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type)) continue; if (item->type&itVolume) { - m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx)); + add_volumes_to_object_in_list(item->obj_idx); ModelObject* obj = object(item->obj_idx); if (obj->volumes.size() == 1) { wxDataViewItem parent = m_objects_model->GetItemById(item->obj_idx); From 5922bf2910e824d160b9e3223f1ec4d6b128858e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Oct 2022 16:59:40 +0200 Subject: [PATCH 84/93] Cut small improvements: * Disable revert icon if cut_plane position wasn't changed * Hide CutGizmo for Simple mode. * Fixed update of bounding box after changing scale during Z axis --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 11 ++++++++++- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 459c6390c..9965e4846 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -968,6 +968,11 @@ bool GLGizmoCut3D::on_is_activable() const return m_parent.get_selection().is_single_full_instance(); } +bool GLGizmoCut3D::on_is_selectable() const +{ + return wxGetApp().get_mode() != comSimple; +} + Vec3d GLGizmoCut3D::mouse_position_in_local_plane(Axis axis, const Linef3& mouse_ray) const { double half_pi = 0.5 * PI; @@ -1194,7 +1199,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* bool GLGizmoCut3D::update_bb() { const BoundingBoxf3 box = bounding_box(); - if (m_max_pos != box.max && m_min_pos != box.min) { + if (m_max_pos != box.max || m_min_pos != box.min) { m_max_pos = box.max; m_min_pos = box.min; m_bb_center = box.center(); @@ -1501,8 +1506,12 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::SameLine(m_label_width); render_move_center_input(Z); ImGui::SameLine(); + + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center; + m_imgui->disabled_begin(is_cut_plane_init); if (render_reset_button("cut_plane", _u8L("Reset cutting plane"))) reset_cut_plane(); + m_imgui->disabled_end(); if (wxGetApp().plater()->printer_technology() == ptFFF) { m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 21d7c64f8..86e14705b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -171,6 +171,7 @@ protected: CommonGizmosDataID on_get_requirements() const override; void on_set_hover_id() override; bool on_is_activable() const override; + bool on_is_selectable() const override; Vec3d mouse_position_in_local_plane(Axis axis, const Linef3&mouse_ray) const; void dragging_grabber_z(const GLGizmoBase::UpdateData &data); void dragging_grabber_xy(const GLGizmoBase::UpdateData &data); From 3a21f156c00144cf9a74ad8ac350901f9ea75352 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Oct 2022 14:13:50 +0200 Subject: [PATCH 85/93] Cut Improvements/Bug Fixing * Context menu: Suppress "Simplify model" for cut object * CutGizmo: * Disable gizmo for dowel object * Invalidate cut plane position after update of Bounding box * Suppress Frustum style for connectors with Dowel type * Rectangle selection: Fixed processing on LeftUp * Selection on Canvas: Suppress to move NEGATIVE_VOLUME if it's a connector * Model:cut: Fixed a bug in add_cut_volume(). Cut info wasn't copied to the new volume --- src/libslic3r/Model.cpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 3 ++- src/slic3r/GUI/GUI_Factories.cpp | 3 +++ src/slic3r/GUI/GUI_ObjectList.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 36 ++++++++++++++++++++++++---- src/slic3r/GUI/Plater.cpp | 9 +++---- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 56f3af2d1..aa21c3ade 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1424,6 +1424,7 @@ static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelV assert(vol->config.id().valid()); assert(vol->config.id() != src_volume->config.id()); vol->set_material(src_volume->material_id(), *src_volume->material()); + vol->cut_info = src_volume->cut_info; } void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix, diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2b5875815..c83e3f970 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3529,7 +3529,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) const int volume_idx = get_first_hover_volume_idx(); BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); volume_bbox.offset(1.0); - if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position)) { + const bool is_cut_connector_selected = m_selection.is_any_connector(); + if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position) && !is_cut_connector_selected) { m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None; // The dragging operation is initiated. m_mouse.drag.move_volume_idx = volume_idx; diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index ff9113c68..6ffe19218 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -1080,6 +1080,9 @@ wxMenu* MenuFactory::multi_selection_menu() wxDataViewItemArray sels; obj_list()->GetSelections(sels); + if (sels.IsEmpty()) + return nullptr; + for (const wxDataViewItem& item : sels) if (!(list_model()->GetItemType(item) & (itVolume | itObject | itInstance))) // show this menu only for Objects(Instances mixed with Objects)/Volumes selection diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d9cc1b1ca..ac9602985 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3806,6 +3806,8 @@ void ObjectList::update_selections_on_canvas() if (sel_cnt == 1) { wxDataViewItem item = GetSelection(); + if (m_objects_model->GetInfoItemType(item) == InfoItemType::CutConnectors) + selection.remove_all(); if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer)) add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, mode); else diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9965e4846..793ab1e51 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -963,9 +963,30 @@ void GLGizmoCut3D::on_set_hover_id() bool GLGizmoCut3D::on_is_activable() const { + const Selection& selection = m_parent.get_selection(); + const int object_idx = selection.get_object_idx(); + if (object_idx < 0) + return false; + + bool is_dowel_object = false; + if (const ModelObject* mo = wxGetApp().plater()->model().objects[object_idx]; mo->is_cut()) { + int solid_connector_cnt = 0; + int connectors_cnt = 0; + for (const ModelVolume* volume : mo->volumes) { + if (volume->is_cut_connector()) { + connectors_cnt++; + if (volume->is_model_part()) + solid_connector_cnt++; + } + if (connectors_cnt > 1) + break; + } + is_dowel_object = connectors_cnt == 1 && solid_connector_cnt == 1; + } + // This is assumed in GLCanvas3D::do_rotate, do not change this // without updating that function too. - return m_parent.get_selection().is_single_full_instance(); + return selection.is_single_full_instance() && !is_dowel_object; } bool GLGizmoCut3D::on_is_selectable() const @@ -1200,6 +1221,9 @@ bool GLGizmoCut3D::update_bb() { const BoundingBoxf3 box = bounding_box(); if (m_max_pos != box.max || m_min_pos != box.min) { + + invalidate_cut_plane(); + m_max_pos = box.max; m_min_pos = box.min; m_bb_center = box.center(); @@ -1412,8 +1436,14 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) if (type_changed) apply_selected_connectors([this, &connectors] (size_t idx) { connectors[idx].attribs.type = CutConnectorType(m_connector_type); }); + m_imgui->disabled_begin(m_connector_type == CutConnectorType::Dowel); + if (type_changed && m_connector_type == CutConnectorType::Dowel) { + m_connector_style = size_t(CutConnectorStyle::Prizm); + apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + } if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style)) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); }); + m_imgui->disabled_end(); if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id)) apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); }); @@ -1850,8 +1880,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) else { // the object is SLA-elevated and the plane is under it. } - - invalidate_cut_plane(); } @@ -2136,7 +2164,7 @@ bool GLGizmoCut3D::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_posi return true; } - if (action == SLAGizmoEventType::LeftUp) { + if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle.is_dragging()) { if ((m_ldown_mouse_position - mouse_position).norm() < 5.) unselect_all_connectors(); return is_selection_changed(alt_down, shift_down); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2051192ee..15ed60a99 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2976,9 +2976,6 @@ void Plater::priv::object_list_changed() const bool model_fits = view3D->get_canvas3d()->check_volumes_outside_state() == ModelInstancePVS_Inside; sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); - - // invalidate CutGizmo after changes in ObjectList - static_cast(q->canvas3D()->get_gizmos_manager().get_gizmo(GLGizmosManager::Cut))->invalidate_cut_plane(); } void Plater::priv::select_all() @@ -4930,8 +4927,12 @@ bool Plater::priv::can_fix_through_netfabb() const bool Plater::priv::can_simplify() const { + const int obj_idx = get_selected_object_idx(); // is object for simplification selected - if (get_selected_object_idx() < 0) return false; + // cut object can't be simplify + if (obj_idx < 0 || model.objects[obj_idx]->is_cut()) + return false; + // is already opened? if (q->canvas3D()->get_gizmos_manager().get_current_type() == GLGizmosManager::EType::Simplify) From 2880704de9060ace26b695b851af60682acc5fb6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Oct 2022 17:33:58 +0200 Subject: [PATCH 86/93] Cut improvements/ bug fixing: * Wrong position of grabber is fixed * OSX specific: ObjectList: Fixed update of the info items after cut * Show info line, when Cut plane is invisible + Fixed non-Win build: added missed include --- src/libslic3r/ObjectID.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 76 +++++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 +- src/slic3r/GUI/ObjectDataViewModel.cpp | 44 +++++---------- src/slic3r/GUI/ObjectDataViewModel.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 7 ++- 6 files changed, 82 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 6be19c88f..4f34572d2 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -2,6 +2,7 @@ #define slic3r_ObjectID_hpp_ #include +#include namespace Slic3r { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 793ab1e51..56dd56462 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -568,6 +568,42 @@ bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::s return revert; } +void GLGizmoCut3D::render_cut_plane_line() +{ + if (cut_line_processing()) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + + const Camera& camera = wxGetApp().plater()->get_camera(); + + Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); + unit_dir.normalize(); + + Vec3d camera_dir = camera.get_dir_forward(); + camera_dir.normalize(); + + if (std::abs(unit_dir.dot(camera_dir)) <= 0.025) { + GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); + if (shader) { + m_circle.reset(); + init_from_circle(m_circle, m_radius * 1.25); + + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; + + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("width", 0.1f); + + m_circle.render(); + + shader->stop_using(); + } + } +} + void GLGizmoCut3D::render_cut_plane() { if (cut_line_processing()) @@ -986,7 +1022,7 @@ bool GLGizmoCut3D::on_is_activable() const // This is assumed in GLCanvas3D::do_rotate, do not change this // without updating that function too. - return selection.is_single_full_instance() && !is_dowel_object; + return selection.is_single_full_instance() && !is_dowel_object && !m_parent.is_layers_editing_enabled(); } bool GLGizmoCut3D::on_is_selectable() const @@ -1153,7 +1189,15 @@ void GLGizmoCut3D::on_stop_dragging() void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/) { - if (force || transformed_bounding_box(true).contains(center_pos)) { + bool can_set_center_pos = force || transformed_bounding_box(true).contains(center_pos); + if (!can_set_center_pos) { + const double old_dist = (m_bb_center - m_plane_center).norm(); + const double new_dist = (m_bb_center - center_pos).norm(); + // check if forcing is reasonable + if ( new_dist < old_dist) + can_set_center_pos = true; + } + if (can_set_center_pos) { m_plane_center = center_pos; m_center_offset = m_plane_center - m_bb_center; } @@ -1178,17 +1222,20 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* // #ysFIXME !!! BoundingBoxf3 ret; - const ModelObject* mo = m_c->selection_info()->model_object(); + const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info(); + if (!sel_info) + return ret; + const ModelObject* mo = sel_info->model_object(); if (!mo) return ret; - const int instance_idx = m_c->selection_info()->get_active_instance(); + const int instance_idx = sel_info->get_active_instance(); if (instance_idx < 0) return ret; const ModelInstance* mi = mo->instances[instance_idx]; const Vec3d& instance_offset = mi->get_offset(); Vec3d cut_center_offset = m_plane_center - instance_offset; - cut_center_offset[Z] -= m_c->selection_info()->get_sla_shift(); + cut_center_offset[Z] -= sel_info->get_sla_shift(); const auto move = assemble_transform(-cut_center_offset); const auto move2 = assemble_transform(m_plane_center); @@ -1330,6 +1377,8 @@ void GLGizmoCut3D::on_render() render_cut_line(); + render_cut_plane_line(); + m_selection_rectangle.render(m_parent); } @@ -1935,7 +1984,7 @@ void GLGizmoCut3D::clear_selection() void GLGizmoCut3D::reset_connectors() { m_c->selection_info()->model_object()->cut_connectors.clear(); - update_model_object(); + update_raycasters_for_picking(); clear_selection(); } @@ -1960,17 +2009,6 @@ void GLGizmoCut3D::update_connector_shape() m_connector_mesh = TriangleMesh(its); } -void GLGizmoCut3D::update_model_object() -{ - const ModelObjectPtrs& mos = wxGetApp().model().objects; - ModelObject* mo = m_c->selection_info()->model_object(); - wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); - - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - - update_raycasters_for_picking(); -} - bool GLGizmoCut3D::cut_line_processing() const { return m_line_beg != Vec3d::Zero(); @@ -2053,7 +2091,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p m_selected.push_back(true); m_selected_count = 1; assert(m_selected.size() == connectors.size()); - update_model_object(); + update_raycasters_for_picking(); m_parent.set_as_dirty(); } else { @@ -2084,7 +2122,7 @@ bool GLGizmoCut3D::delete_selected_connectors(CutConnectors& connectors) m_selected_count = 0; assert(m_selected.size() == connectors.size()); - update_model_object(); + update_raycasters_for_picking(); m_parent.set_as_dirty(); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 86e14705b..941d01660 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -237,6 +237,7 @@ private: void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); void render_cut_plane_grabbers(); void render_cut_line(); + void render_cut_plane_line(); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool force = false); bool update_bb(); @@ -248,7 +249,6 @@ private: void init_connector_shapes(); void update_connector_shape(); void validate_connector_settings(); - void update_model_object(); bool process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position); }; diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index e2fca5fae..50577771b 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -34,6 +34,14 @@ void ObjectDataViewModelNode::init_container() #endif //__WXGTK__ } +void ObjectDataViewModelNode::invalidate_container() +{ +#ifndef __WXGTK__ + if (this->GetChildCount() == 0) + this->m_container = false; +#endif //__WXGTK__ +} + static constexpr char LayerRootIcon[] = "edit_layers_all"; static constexpr char LayerIcon[] = "edit_layers_some"; static constexpr char WarningIcon[] = "exclamation"; @@ -724,10 +732,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) delete node_parent; ret_item = wxDataViewItem(obj_node); -#ifndef __WXGTK__ - if (obj_node->GetChildCount() == 0) - obj_node->m_container = false; -#endif //__WXGTK__ + obj_node->invalidate_container(); ItemDeleted(ret_item, wxDataViewItem(node_parent)); return ret_item; } @@ -743,10 +748,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) delete node_parent; ret_item = wxDataViewItem(obj_node); -#ifndef __WXGTK__ - if (obj_node->GetChildCount() == 0) - obj_node->m_container = false; -#endif //__WXGTK__ + obj_node->invalidate_container(); ItemDeleted(ret_item, wxDataViewItem(node_parent)); return ret_item; } @@ -768,10 +770,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) node_parent->m_volumes_cnt = 0; delete last_child_node; -#ifndef __WXGTK__ - if (node_parent->GetChildCount() == 0) - node_parent->m_container = false; -#endif //__WXGTK__ + node_parent->invalidate_container(); ItemDeleted(parent, wxDataViewItem(last_child_node)); wxCommandEvent event(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED); @@ -806,10 +805,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) // set m_containet to FALSE if parent has no child if (node_parent) { -#ifndef __WXGTK__ - if (node_parent->GetChildCount() == 0) - node_parent->m_container = false; -#endif //__WXGTK__ + node_parent->invalidate_container(); ret_item = parent; } @@ -851,10 +847,7 @@ wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &par parent_node->set_printable_icon(last_inst_printable); ItemDeleted(parent_item, inst_root_item); ItemChanged(parent_item); -#ifndef __WXGTK__ - if (parent_node->GetChildCount() == 0) - parent_node->m_container = false; -#endif //__WXGTK__ + parent_node->invalidate_container(); } // update object_node printable property @@ -899,10 +892,7 @@ void ObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) ItemDeleted(parent, item); } - // set m_containet to FALSE if parent has no child -#ifndef __WXGTK__ - root->m_container = false; -#endif //__WXGTK__ + root->invalidate_container(); } void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) @@ -932,11 +922,7 @@ void ObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent) ItemDeleted(parent, item); } root->m_volumes_cnt = 0; - - // set m_containet to FALSE if parent has no child -#ifndef __WXGTK__ - root->m_container = false; -#endif //__WXGTK__ + root->invalidate_container(); } void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent) diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 55dbaafe2..6f2d1519c 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -128,7 +128,9 @@ public: } void init_container(); - bool IsContainer() const + void invalidate_container(); + + bool IsContainer() const { return m_container; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 15ed60a99..d3bd704d2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4470,7 +4470,10 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&) void Plater::priv::on_action_layersediting(SimpleEvent&) { - view3D->enable_layers_editing(!view3D->is_layers_editing_enabled()); + const bool enable_layersediting = !view3D->is_layers_editing_enabled(); + view3D->enable_layers_editing(enable_layersediting); + if (enable_layersediting) + view3D->get_canvas3d()->reset_all_gizmos(); notification_manager->set_move_from_overlay(view3D->is_layers_editing_enabled()); } @@ -4513,7 +4516,7 @@ void Plater::priv::on_right_click(RBtnEvent& evt) selection.is_single_full_object() || selection.is_multiple_full_instance(); #if ENABLE_WORLD_COORDINATE - const bool is_part = selection.is_single_volume_or_modifier(); + const bool is_part = selection.is_single_volume_or_modifier() && ! selection.is_any_connector(); #else const bool is_part = selection.is_single_volume() || selection.is_single_modifier(); #endif // ENABLE_WORLD_COORDINATE From 7bb0b7eefc3dc4bc5fdb10da5c3afa469a2843e1 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 20 Oct 2022 16:34:21 +0200 Subject: [PATCH 87/93] Cut bug fixing: Fixed a place of connectors after several cutting + Added info about camera direction to a DEBUG window + Code factoring (deleted unused code) + Fixed build warnings --- src/libslic3r/Model.cpp | 6 ++- src/slic3r/GUI/Field.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.cpp | 11 +++--- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 52 ++++++++++++++++--------- src/slic3r/GUI/SavePresetDialog.cpp | 2 +- src/slic3r/GUI/UnsavedChangesDialog.hpp | 2 +- 6 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index aa21c3ade..3c4e00454 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1232,6 +1232,8 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn case CutConnectorShape::Hexagon: sectorCount = 6; break; + default: + break; } if (connector_attributes.style == CutConnectorStyle::Prizm) @@ -1395,11 +1397,11 @@ void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttri void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower) { - const auto volume_matrix = volume->get_matrix(); + const auto volume_matrix = instance_matrix * volume->get_matrix(); // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. - volume->set_transformation(Geometry::Transformation(instance_matrix * volume_matrix)); + volume->set_transformation(Geometry::Transformation(volume_matrix)); // Some logic for the negative volumes/connectors. Add only needed modifiers auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index e674b52d0..bedb3718e 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -33,7 +33,7 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/) // Style_NoTrailingZeroes does not work on OSX. It also does not work correctly with some locales on Windows. // return wxNumberFormatter::ToString(value, max_precision, wxNumberFormatter::Style_NoTrailingZeroes); - wxString s = wxNumberFormatter::ToString(value, value < 0.0001 ? 10 : max_precision, wxNumberFormatter::Style_None); + wxString s = wxNumberFormatter::ToString(value, std::abs(value) < 0.0001 ? 10 : max_precision, wxNumberFormatter::Style_None); // The following code comes from wxNumberFormatter::RemoveTrailingZeroes(wxString& s) // with the exception that here one sets the decimal separator explicitely to dot. diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index ac9602985..5be1efed1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2990,9 +2990,9 @@ void ObjectList::delete_instance_from_list(const size_t obj_idx, const size_t in void ObjectList::update_lock_icons_for_model() { - for (int obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) + for (size_t obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) if (!(*m_objects)[obj_idx]->is_cut()) - m_objects_model->UpdateLockIcon(m_objects_model->GetItemById(obj_idx), false); + m_objects_model->UpdateLockIcon(m_objects_model->GetItemById(int(obj_idx)), false); } bool ObjectList::delete_from_model_and_list(const ItemType type, const int obj_idx, const int sub_obj_idx) @@ -4088,15 +4088,16 @@ bool ObjectList::fix_cut_selection(wxDataViewItemArray& sels) bool is_instance_selection = m_objects_model->GetItemType(item) & itInstance; - int obj_idx = m_objects_model->GetObjectIdByItem(item); + int object_idx = m_objects_model->GetObjectIdByItem(item); int inst_idx = is_instance_selection ? m_objects_model->GetInstanceIdByItem(item) : 0; - if (auto obj = object(obj_idx); obj->is_cut()) { + if (auto obj = object(object_idx); obj->is_cut()) { sels.Clear(); auto cut_id = obj->cut_id; - for (int obj_idx = 0; obj_idx < (*m_objects).size(); ++obj_idx) { + int objects_cnt = int((*m_objects).size()); + for (int obj_idx = 0; obj_idx < objects_cnt; ++obj_idx) { auto object = (*m_objects)[obj_idx]; if (object->is_cut() && object->cut_id.has_same_id(cut_id)) sels.Add(is_instance_selection ? m_objects_model->GetItemByInstanceId(obj_idx, inst_idx) : m_objects_model->GetItemById(obj_idx)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 56dd56462..fcb5cc247 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -578,13 +578,9 @@ void GLGizmoCut3D::render_cut_plane_line() const Camera& camera = wxGetApp().plater()->get_camera(); - Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); - unit_dir.normalize(); + const Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); - Vec3d camera_dir = camera.get_dir_forward(); - camera_dir.normalize(); - - if (std::abs(unit_dir.dot(camera_dir)) <= 0.025) { + if (std::abs(unit_dir.dot(camera.get_dir_forward())) <= 0.025) { GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); if (shader) { m_circle.reset(); @@ -665,7 +661,6 @@ void GLGizmoCut3D::render_line(GLModel& line_model, const ColorRGBA& color, Tran if (shader) { shader->start_using(); - const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", wxGetApp().plater()->get_camera().get_projection_matrix()); shader->set_uniform("width", width); @@ -734,11 +729,11 @@ void GLGizmoCut3D::render_cut_plane_grabbers() // render Z grabber - if ((!m_dragging && m_hover_id < 0)) + if (!m_dragging && m_hover_id < 0) render_grabber_connection(color, view_matrix); render_model(m_sphere.model, color, view_matrix * scale_transform(size)); - if (!m_dragging && m_hover_id < 0 || m_hover_id == Z) + if ((!m_dragging && m_hover_id < 0) || m_hover_id == Z) { const BoundingBoxf3 tbb = transformed_bounding_box(); if (tbb.min.z() <= 0.0) @@ -867,9 +862,9 @@ void GLGizmoCut3D::on_set_state() m_parent.request_extra_frame(); } - else + else { m_c->object_clipper()->release(); - + } force_update_clipper_on_render = m_state == On; } @@ -1398,6 +1393,34 @@ void GLGizmoCut3D::render_debug_input_window() if (auto oc = m_c->object_clipper()) oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width)); + ImGui::Separator(); + + // Camera editing + + auto get_label = [](Vec3d dir) { + wxString str = "x=" + double_to_string(dir.x(), 2) + + ", y=" + double_to_string(dir.y(), 2) + + ", z=" + double_to_string(dir.z(), 2); + return str; + }; + + const Camera& camera = wxGetApp().plater()->get_camera(); + + Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); + m_imgui->text("Unit dir: "); + ImGui::SameLine(m_label_width); + m_imgui->text(get_label(unit_dir)); + + Vec3d camera_dir = camera.get_dir_forward(); + m_imgui->text("Camera dir: "); + ImGui::SameLine(m_label_width); + m_imgui->text(get_label(camera_dir)); + + m_imgui->text("Unit2Camera: "); + double proj = unit_dir.dot(camera_dir); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(std::abs(proj) <= 0.025 ? ImGuiWrapper::COL_ORANGE_LIGHT : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), double_to_string(proj, 2)); + m_imgui->end(); } @@ -2021,13 +2044,6 @@ void GLGizmoCut3D::discard_cut_line_processing() bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse_position) { - const float sla_shift = m_c->selection_info()->get_sla_shift(); - const ModelObject* mo = m_c->selection_info()->model_object(); - const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; - Transform3d inst_trafo = sla_shift > 0.f ? - assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : - mi->get_transformation().get_matrix(); - const Camera& camera = wxGetApp().plater()->get_camera(); Vec3d pt; diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 57aa5da98..40f981722 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -163,7 +163,7 @@ void SavePresetDialog::Item::update() if (m_valid_type == ValidationType::Valid && existing) { if (m_preset_name == m_presets->get_selected_preset_name()) { - if (!rename && m_presets->get_edited_preset().is_dirty || + if ((!rename && m_presets->get_edited_preset().is_dirty) || m_parent->get_preset_bundle()) // means that we save modifications from the DiffDialog info_line = _L("Save preset modifications to existing user profile"); else diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 69f808451..a0b53dadf 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -414,7 +414,7 @@ public: std::string get_left_preset_name(Preset::Type type); std::string get_right_preset_name(Preset::Type type); - std::vector get_selected_options(Preset::Type type) const { return std::move(m_tree->options(type, true)); } + std::vector get_selected_options(Preset::Type type) const { return m_tree->options(type, true); } protected: void on_dpi_changed(const wxRect& suggested_rect) override; From d7db5bde1ab04a534e824651af75cf82f6d5709b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 21 Oct 2022 09:06:00 +0200 Subject: [PATCH 88/93] Fixed z-fighting between cut contours and cut plane --- src/slic3r/GUI/MeshUtils.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index b6f95eead..d8a9474b3 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -243,6 +243,8 @@ void MeshClipper::recalculate_triangles() } tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting + Transform3d tr2 = tr; + tr2.pretranslate(0.002 * m_plane.get_normal().normalized()); #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -318,9 +320,9 @@ void MeshClipper::recalculate_triangles() // vertices + indices for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) { - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); - init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast(), (Vec3f)up.cast()); + init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast(), (Vec3f)up.cast()); const size_t idx = it - triangles2d.cbegin(); init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2); } From 98d7fe335b7cd912dfa12b1374b15a82193c548e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 21 Oct 2022 16:07:41 +0200 Subject: [PATCH 89/93] Cut WIP: experiments with detection of the position for CutPlaneLine Note: It still doesn't work properly + CurGizmo: Fixed a check of new center position in function set_center_pos(). --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 134 ++++++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 +- 2 files changed, 94 insertions(+), 43 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index fcb5cc247..d8973228a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -224,7 +224,7 @@ std::string GLGizmoCut3D::get_tooltip() const if (m_hover_id == Z) { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm")); - const BoundingBoxf3 tbb = transformed_bounding_box(); + const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); if (tbb.max.z() >= 0.0) { double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef; tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")"; @@ -568,6 +568,82 @@ bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::s return revert; } +static Vec2d ndc_to_ss(const Vec3d& ndc, const std::array& viewport) { + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + return { half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h }; +}; +static Vec3d clip_to_ndc(const Vec4d& clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); +} +static Vec4d world_to_clip(const Vec3d& world, const Matrix4d& projection_view_matrix) { + return projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0); +} +static Vec2d world_to_ss(const Vec3d& world, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(world, projection_view_matrix)), viewport); +} + +static wxString get_label(Vec3d vec) +{ + wxString str = "x=" + double_to_string(vec.x(), 2) + + ", y=" + double_to_string(vec.y(), 2) + + ", z=" + double_to_string(vec.z(), 2); + return str; +} + +static wxString get_label(Vec2d vec) +{ + wxString str = "x=" + double_to_string(vec.x(), 2) + + ", y=" + double_to_string(vec.y(), 2); + return str; +} + +bool GLGizmoCut3D::can_render_cut_plane_line(bool render_values_in_debug/* = false*/) +{ + const Camera& camera = wxGetApp().plater()->get_camera(); + + Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); + + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + + const Vec2d screen_coord = world_to_ss(m_plane_center, projection_view_matrix, camera.get_viewport()); + + const Vec3d mouse_dir = m_parent.mouse_ray(Point(screen_coord.x(), screen_coord.y())).unit_vector(); + + const Vec3d camera_dir = camera.get_dir_forward(); +// double proj = unit_dir.dot(camera_dir); + const double proj = unit_dir.dot(mouse_dir); + const bool can_render = std::abs(proj) <= 0.01; // 0.25 + + if (render_values_in_debug) { + ImGui::Separator(); + + m_imgui->text("Unit dir: "); + ImGui::SameLine(m_label_width); + m_imgui->text(get_label(unit_dir)); + + m_imgui->text("Camera dir: "); + ImGui::SameLine(m_label_width); + m_imgui->text(get_label(camera_dir)); + + // m_imgui->text("screen_coord: "); + // ImGui::SameLine(m_label_width); + // m_imgui->text(get_label(screen_coord)); + + m_imgui->text("Mouse dir: "); + ImGui::SameLine(m_label_width); + m_imgui->text(get_label(mouse_dir)); + +// m_imgui->text("Unit2Camera: "); + m_imgui->text("Unit2Mouse: "); + double proj = unit_dir.dot(/*camera_dir*/mouse_dir); + ImGui::SameLine(m_label_width); + m_imgui->text_colored(can_render ? ImGuiWrapper::COL_ORANGE_LIGHT : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), double_to_string(proj, 2)); + } + + return can_render; +} + void GLGizmoCut3D::render_cut_plane_line() { if (cut_line_processing()) @@ -576,16 +652,13 @@ void GLGizmoCut3D::render_cut_plane_line() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - const Camera& camera = wxGetApp().plater()->get_camera(); - - const Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); - - if (std::abs(unit_dir.dot(camera.get_dir_forward())) <= 0.025) { + if (can_render_cut_plane_line()) { GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); if (shader) { - m_circle.reset(); - init_from_circle(m_circle, m_radius * 1.25); + GLModel circle; + init_from_circle(circle, m_radius * 1.25); + const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; shader->start_using(); @@ -593,7 +666,7 @@ void GLGizmoCut3D::render_cut_plane_line() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("width", 0.1f); - m_circle.render(); + circle.render(); shader->stop_using(); } @@ -735,7 +808,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() if ((!m_dragging && m_hover_id < 0) || m_hover_id == Z) { - const BoundingBoxf3 tbb = transformed_bounding_box(); + const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); if (tbb.min.z() <= 0.0) render_model(m_cone.model, color, view_matrix * assemble_transform(-offset, PI * Vec3d::UnitX(), cone_scale)); @@ -1184,7 +1257,9 @@ void GLGizmoCut3D::on_stop_dragging() void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/) { - bool can_set_center_pos = force || transformed_bounding_box(true).contains(center_pos); + const BoundingBoxf3 tbb = transformed_bounding_box(center_pos); + + bool can_set_center_pos = force || (tbb.max.z() > -1. && tbb.min.z() < 1.); if (!can_set_center_pos) { const double old_dist = (m_bb_center - m_plane_center).norm(); const double new_dist = (m_bb_center - center_pos).norm(); @@ -1212,7 +1287,7 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const return ret; } -BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false*/) const +BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, bool revert_move /*= false*/) const { // #ysFIXME !!! BoundingBoxf3 ret; @@ -1229,11 +1304,11 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(bool revert_move /*= false* const ModelInstance* mi = mo->instances[instance_idx]; const Vec3d& instance_offset = mi->get_offset(); - Vec3d cut_center_offset = m_plane_center - instance_offset; + Vec3d cut_center_offset = plane_center - instance_offset; cut_center_offset[Z] -= sel_info->get_sla_shift(); const auto move = assemble_transform(-cut_center_offset); - const auto move2 = assemble_transform(m_plane_center); + const auto move2 = assemble_transform(plane_center); const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move; @@ -1393,33 +1468,8 @@ void GLGizmoCut3D::render_debug_input_window() if (auto oc = m_c->object_clipper()) oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width)); - ImGui::Separator(); - // Camera editing - - auto get_label = [](Vec3d dir) { - wxString str = "x=" + double_to_string(dir.x(), 2) + - ", y=" + double_to_string(dir.y(), 2) + - ", z=" + double_to_string(dir.z(), 2); - return str; - }; - - const Camera& camera = wxGetApp().plater()->get_camera(); - - Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); - m_imgui->text("Unit dir: "); - ImGui::SameLine(m_label_width); - m_imgui->text(get_label(unit_dir)); - - Vec3d camera_dir = camera.get_dir_forward(); - m_imgui->text("Camera dir: "); - ImGui::SameLine(m_label_width); - m_imgui->text(get_label(camera_dir)); - - m_imgui->text("Unit2Camera: "); - double proj = unit_dir.dot(camera_dir); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(std::abs(proj) <= 0.025 ? ImGuiWrapper::COL_ORANGE_LIGHT : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), double_to_string(proj, 2)); + can_render_cut_plane_line(true); m_imgui->end(); } @@ -1549,7 +1599,7 @@ void GLGizmoCut3D::render_build_size() { double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0; wxString unit_str = " " + (m_imperial_units ? _L("in") : _L("mm")); - const BoundingBoxf3 tbb = transformed_bounding_box(); + const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center); Vec3d tbb_sz = tbb.size(); wxString size = "X: " + double_to_string(tbb_sz.x() * koef, 2) + unit_str + @@ -1880,7 +1930,7 @@ bool GLGizmoCut3D::can_perform_cut() const if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; - const BoundingBoxf3 tbb = transformed_bounding_box(true); + const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center, true); return tbb.contains(m_plane_center); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 941d01660..95cd10fa8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -160,7 +160,7 @@ public: void invalidate_cut_plane(); BoundingBoxf3 bounding_box() const; - BoundingBoxf3 transformed_bounding_box(bool revert_move = false) const; + BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, bool revert_move = false) const; protected: bool on_init() override; @@ -237,6 +237,7 @@ private: void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); void render_cut_plane_grabbers(); void render_cut_line(); + bool can_render_cut_plane_line(bool render_values = false); void render_cut_plane_line(); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool force = false); From ae2166778605efd022ef78f1d803fb78be12912c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 24 Oct 2022 16:57:02 +0200 Subject: [PATCH 90/93] Cut WIP: First implementation for detection of the invalid connectors position Implemented cases: * overlap of some connectors * check if some connector position is outside of clipper --- src/libslic3r/libslic3r.h | 4 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 110 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 58 ++++++++++++ src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 + src/slic3r/GUI/MeshUtils.cpp | 36 ++++++++ src/slic3r/GUI/MeshUtils.hpp | 2 + 7 files changed, 158 insertions(+), 55 deletions(-) diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 2285c29a6..054ccd4ea 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -262,9 +262,9 @@ constexpr inline T lerp(const T& a, const T& b, Number t) } template -constexpr inline bool is_approx(Number value, Number test_value) +constexpr inline bool is_approx(Number value, Number test_value, Number precision = EPSILON) { - return std::fabs(double(value) - double(test_value)) < double(EPSILON); + return std::fabs(double(value) - double(test_value)) < double(precision); } // A meta-predicate which is true for integers wider than or equal to coord_t diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d8973228a..7695a902d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1845,6 +1845,32 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c return assemble_transform(offset, Vec3d::Zero(), Vec3d::Ones() - border_scale, Vec3d::Ones()) * vol_matrix; } +bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) +{ + // check if connector pos is out of clipping plane + if (m_c->object_clipper() && !m_c->object_clipper()->containes(cur_pos)) + return true; + + const CutConnector& cur_connector = connectors[idx]; + const Transform3d matrix = translation_transform(cur_pos) * m_rotation_m * + scale_transform(Vec3f(cur_connector.radius, cur_connector.radius, cur_connector.height).cast()); + const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix); + + if (!bounding_box().contains(cur_tbb)) + return true; + + for (size_t i = 0; i < connectors.size(); ++i) { + if (i == idx) + continue; + const CutConnector& connector = connectors[i]; + + if ((connector.pos - cur_connector.pos).norm() < double(connector.radius + cur_connector.radius)) + return true; + } + + return false; +} + void GLGizmoCut3D::render_connectors() { ::glEnable(GL_DEPTH_TEST); @@ -1872,8 +1898,6 @@ void GLGizmoCut3D::render_connectors() const ClippingPlane* cp = m_c->object_clipper()->get_clipping_plane(); const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; - const Transform3d instance_trafo = assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix(); - m_has_invalid_connector = false; for (size_t i = 0; i < connectors.size(); ++i) { @@ -1881,16 +1905,14 @@ void GLGizmoCut3D::render_connectors() float height = connector.height; // recalculate connector position to world position - Vec3d pos = connector.pos + instance_offset; - if (connector.attribs.type == CutConnectorType::Dowel && - connector.attribs.style == CutConnectorStyle::Prizm) { - pos -= height * normal; - height *= 2; - } - pos += sla_shift * Vec3d::UnitZ(); + Vec3d pos = connector.pos + instance_offset + sla_shift * Vec3d::UnitZ(); // First decide about the color of the point. - if (!m_connectors_editing) + if (is_conflict_for_connector(i, connectors, pos)) { + m_has_invalid_connector = true; + render_color = CONNECTOR_ERR_COLOR; + } + else if (!m_connectors_editing) render_color = CONNECTOR_ERR_COLOR; else if (size_t(m_hover_id - m_connectors_group_id) == i) render_color = connector.attribs.type == CutConnectorType::Dowel ? HOVERED_DOWEL_COLOR : HOVERED_PLAG_COLOR; @@ -1898,26 +1920,13 @@ void GLGizmoCut3D::render_connectors() render_color = connector.attribs.type == CutConnectorType::Dowel ? SELECTED_DOWEL_COLOR : SELECTED_PLAG_COLOR; else // neither hover nor picking render_color = connector.attribs.type == CutConnectorType::Dowel ? DOWEL_COLOR : PLAG_COLOR; - // ! #ysFIXME rework get_volume_transformation - if (0) { // else { // neither hover nor picking - int mesh_id = -1; - for (const ModelVolume* mv : mo->volumes) { - ++mesh_id; - if (!mv->is_model_part()) - continue; - - const Transform3d volume_trafo = get_volume_transformation(mv); - - if (m_c->raycaster()->raycasters()[mesh_id]->is_valid_intersection(pos, -normal, instance_trafo * volume_trafo)) { - render_color = m_connectors_editing ? ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f) : CONNECTOR_ERR_COLOR; - break; - } - render_color = CONNECTOR_ERR_COLOR; - m_has_invalid_connector = true; - } - } const Camera& camera = wxGetApp().plater()->get_camera(); + if (connector.attribs.type == CutConnectorType::Dowel && + connector.attribs.style == CutConnectorStyle::Prizm) { + pos -= height * normal; + height *= 2; + } const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); @@ -1930,8 +1939,8 @@ bool GLGizmoCut3D::can_perform_cut() const if (m_has_invalid_connector || (!m_keep_upper && !m_keep_lower) || m_connectors_editing) return false; - const BoundingBoxf3 tbb = transformed_bounding_box(m_plane_center, true); - return tbb.contains(m_plane_center); + const auto clipper = m_c->object_clipper(); + return clipper && clipper->has_valid_contour(); } void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowels_as_separate_object) @@ -1982,7 +1991,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= sla_shift_z; - if (0.0 < object_cut_z && can_perform_cut()) { + if (0.0 < object_cut_z) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); bool create_dowels_as_separate_object = false; @@ -2014,7 +2023,7 @@ bool GLGizmoCut3D::unproject_on_cut_plane(const Vec2d& mouse_position, std::pair const ModelObject* mo = m_c->selection_info()->model_object(); const ModelInstance* mi = mo->instances[m_c->selection_info()->get_active_instance()]; - const Transform3d instance_trafo = sla_shift > 0.0 ? + const Transform3d instance_trafo = sla_shift > 0.f ? assemble_transform(Vec3d(0.0, 0.0, sla_shift)) * mi->get_transformation().get_matrix() : mi->get_transformation().get_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); @@ -2138,33 +2147,28 @@ bool GLGizmoCut3D::process_cut_line(SLAGizmoEventType action, const Vec2d& mouse bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_position) { + if (!m_connectors_editing) + return false; + std::pair pos_and_normal; Vec3d pos_world; if (unproject_on_cut_plane(mouse_position.cast(), pos_and_normal, pos_world)) { const Vec3d& hit = pos_and_normal.first; - if (m_connectors_editing) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); + unselect_all_connectors(); - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add connector"), UndoRedo::SnapshotType::GizmoAction); - unselect_all_connectors(); - - connectors.emplace_back(hit, m_rotation_m, - m_connector_size * 0.5f, m_connector_depth_ratio, - m_connector_size_tolerance, m_connector_depth_ratio_tolerance, - CutConnectorAttributes( CutConnectorType(m_connector_type), - CutConnectorStyle(m_connector_style), - CutConnectorShape(m_connector_shape_id))); - m_selected.push_back(true); - m_selected_count = 1; - assert(m_selected.size() == connectors.size()); - update_raycasters_for_picking(); - m_parent.set_as_dirty(); - } - else { - // Following would inform the clipper about the mouse click, so it can - // toggle the respective contour as disabled. - //m_c->object_clipper()->pass_mouse_click(pos_world); - } + connectors.emplace_back(hit, m_rotation_m, + m_connector_size * 0.5f, m_connector_depth_ratio, + m_connector_size_tolerance, m_connector_depth_ratio_tolerance, + CutConnectorAttributes( CutConnectorType(m_connector_type), + CutConnectorStyle(m_connector_style), + CutConnectorShape(m_connector_shape_id))); + m_selected.push_back(true); + m_selected_count = 1; + assert(m_selected.size() == connectors.size()); + update_raycasters_for_picking(); + m_parent.set_as_dirty(); return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 95cd10fa8..e33c5c897 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -223,6 +223,7 @@ private: bool render_reset_button(const std::string& label_id, const std::string& tooltip) const; bool render_connect_type_radio_button(CutConnectorType type); Transform3d get_volume_transformation(const ModelVolume* volume) const; + bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); void render_connectors(); bool can_perform_cut() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index a08836c28..435d8dc6e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -456,6 +456,64 @@ void ObjectClipper::render_cut() const } } +bool ObjectClipper::containes(Vec3d point) const +{ + if (m_clp_ratio == 0.) + return false; + const SelectionInfo* sel_info = get_pool()->selection_info(); + int sel_instance_idx = sel_info->get_active_instance(); + if (sel_instance_idx < 0) + return false; + const ModelObject* mo = sel_info->model_object(); + const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); + + size_t clipper_id = 0; + for (const ModelVolume* mv : mo->volumes) { + const Geometry::Transformation vol_trafo = mv->get_transformation(); + Geometry::Transformation trafo = inst_trafo * vol_trafo; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + + auto& clipper = m_clippers[clipper_id]; + clipper->set_plane(*m_clp); + clipper->set_transformation(trafo); + clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + if (clipper->contains(point)) + return true; + + ++clipper_id; + } + return false; +} + +bool ObjectClipper::has_valid_contour() const +{ + if (m_clp_ratio == 0.) + return false; + const SelectionInfo* sel_info = get_pool()->selection_info(); + int sel_instance_idx = sel_info->get_active_instance(); + if (sel_instance_idx < 0) + return false; + const ModelObject* mo = sel_info->model_object(); + const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); + + size_t clipper_id = 0; + for (const ModelVolume* mv : mo->volumes) { + const Geometry::Transformation vol_trafo = mv->get_transformation(); + Geometry::Transformation trafo = inst_trafo * vol_trafo; + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); + + auto& clipper = m_clippers[clipper_id]; + clipper->set_plane(*m_clp); + clipper->set_transformation(trafo); + clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); + if (clipper->has_valid_contour()) + return true; + + ++clipper_id; + } + return false; +} + void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) { const ModelObject* mo = get_pool()->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 7c41a64b9..79483e403 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -266,6 +266,8 @@ public: void pass_mouse_click(const Vec3d& pt); std::vector get_disabled_contours() const; + bool containes(Vec3d point) const; + bool has_valid_contour() const; protected: diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index d8a9474b3..f711ecf03 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -146,6 +146,42 @@ void MeshClipper::render_contour() #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +bool MeshClipper::contains(Vec3d point) +{ + if (!m_result) + recalculate_triangles(); + + for (CutIsland& isl : m_result->cut_islands) { + BoundingBoxf3 bb = isl.model_expanded.get_bounding_box(); + + // instead of using of standard bb.contains(point) + // because of precision (Note, that model_expanded is pretranslate(0.003 * normal.normalized())) + constexpr double pres = 0.01; + bool ret = (point.x() > bb.min.x() || is_approx(point.x(), bb.min.x(), pres)) && (point.x() < bb.max.x() || is_approx(point.x(), bb.max.x(), pres)) + && (point.y() > bb.min.y() || is_approx(point.y(), bb.min.y(), pres)) && (point.y() < bb.max.y() || is_approx(point.y(), bb.max.y(), pres)) + && (point.z() > bb.min.z() || is_approx(point.z(), bb.min.z(), pres)) && (point.z() < bb.max.z() || is_approx(point.z(), bb.max.z(), pres)); + if (ret) { + // when we detected, that model_expanded's bb contains a point, then check if its polygon contains this point + Vec3d point_inv = m_result->trafo.inverse() * point; + Point pt = Point(scale_(point_inv.x()), scale_(point_inv.y())); + if (isl.expoly.contains(pt)) + return true; + } + } + return false; +} + +bool MeshClipper::has_valid_contour() +{ + if (!m_result) + recalculate_triangles(); + + for (CutIsland& isl : m_result->cut_islands) + if (isl.model_expanded.get_bounding_box().defined) + return true; + + return false; +} void MeshClipper::pass_mouse_click(const Vec3d& point_in) diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 726f228ca..186b74feb 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -115,6 +115,8 @@ public: void pass_mouse_click(const Vec3d& pt); + bool contains(Vec3d point); + bool has_valid_contour(); private: void recalculate_triangles(); From 18edc71254076326ad470280e3f8115607eedd86 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 25 Oct 2022 15:54:52 +0200 Subject: [PATCH 91/93] Cut WIP: Code refactoring for https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/ae2166778605efd022ef78f1d803fb78be12912c + ObjectList: Fixed list of the types for "Change type" dialog, when object is cut. + CutGizmo: * Warning line is extended for information about invalid connectors * Fixed a crash on undo/Redo, when cutGizmo is active --- src/slic3r/GUI/GUI_ObjectList.cpp | 16 +++++-- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 59 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 13 ++++++ src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 54 ++-------------------- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 +- src/slic3r/GUI/MeshUtils.cpp | 38 ++++----------- src/slic3r/GUI/MeshUtils.hpp | 4 +- 7 files changed, 82 insertions(+), 104 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5be1efed1..4c83e1f0d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4155,10 +4155,11 @@ void ObjectList::change_part_type() if (obj_idx < 0) return; const ModelVolumeType type = volume->type(); + const ModelObject* obj = object(obj_idx); if (type == ModelVolumeType::MODEL_PART) { int model_part_cnt = 0; - for (auto vol : (*m_objects)[obj_idx]->volumes) { + for (auto vol : obj->volumes) { if (vol->type() == ModelVolumeType::MODEL_PART) ++model_part_cnt; } @@ -4169,9 +4170,18 @@ void ObjectList::change_part_type() } } - const wxString names[] = { _L("Part"), _L("Negative Volume"), _L("Modifier"), _L("Support Blocker"), _L("Support Enforcer") }; - auto new_type = ModelVolumeType(wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), wxArrayString(5, names), int(type))); + const bool is_cut_object = obj->is_cut(); + wxArrayString names; + names.Alloc(is_cut_object ? 3 : 5); + if (!is_cut_object) + for (const wxString& type : { _L("Part"), _L("Negative Volume") }) + names.Add(type); + for (const wxString& type : { _L("Modifier"), _L("Support Blocker"), _L("Support Enforcer") } ) + names.Add(type); + + const int type_shift = is_cut_object ? 2 : 0; + auto new_type = ModelVolumeType(type_shift + wxGetApp().GetSingleChoiceIndex(_L("Type:"), _L("Select type of part"), names, int(type) - type_shift)); if (new_type == type || new_type == ModelVolumeType::INVALID) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7695a902d..802cd3006 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -928,6 +928,7 @@ void GLGizmoCut3D::on_set_state() { if (m_state == On) { update_bb(); + m_connectors_editing = !m_selected.empty(); // initiate archived values m_ar_plane_center = m_plane_center; @@ -937,6 +938,7 @@ void GLGizmoCut3D::on_set_state() } else { m_c->object_clipper()->release(); + m_selected.clear(); } force_update_clipper_on_render = m_state == On; } @@ -1257,16 +1259,20 @@ void GLGizmoCut3D::on_stop_dragging() void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/) { - const BoundingBoxf3 tbb = transformed_bounding_box(center_pos); - - bool can_set_center_pos = force || (tbb.max.z() > -1. && tbb.min.z() < 1.); + bool can_set_center_pos = force; if (!can_set_center_pos) { - const double old_dist = (m_bb_center - m_plane_center).norm(); - const double new_dist = (m_bb_center - center_pos).norm(); - // check if forcing is reasonable - if ( new_dist < old_dist) + const BoundingBoxf3 tbb = transformed_bounding_box(center_pos); + if (tbb.max.z() > -1. && tbb.min.z() < 1.) can_set_center_pos = true; + else { + const double old_dist = (m_bb_center - m_plane_center).norm(); + const double new_dist = (m_bb_center - center_pos).norm(); + // check if forcing is reasonable + if (new_dist < old_dist) + can_set_center_pos = true; + } } + if (can_set_center_pos) { m_plane_center = center_pos; m_center_offset = m_plane_center - m_bb_center; @@ -1299,7 +1305,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, if (!mo) return ret; const int instance_idx = sel_info->get_active_instance(); - if (instance_idx < 0) + if (instance_idx < 0 || mo->instances.empty()) return ret; const ModelInstance* mi = mo->instances[instance_idx]; @@ -1372,7 +1378,6 @@ bool GLGizmoCut3D::update_bb() clear_selection(); if (CommonGizmosDataObjects::SelectionInfo* selection = m_c->selection_info()) m_selected.resize(selection->model_object()->cut_connectors.size(), false); - m_connectors_editing = !m_selected.empty(); return true; } @@ -1791,8 +1796,18 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) void GLGizmoCut3D::render_input_window_warning() const { - if (wxGetApp().plater()->printer_technology() == ptFFF && m_has_invalid_connector) - m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected.")); + if (wxGetApp().plater()->printer_technology() == ptFFF && m_has_invalid_connector) { + wxString out = wxString(ImGui::WarningMarkerSmall) + _L("Invalid connectors detected") + ":"; + if (m_info_stats.outside_cut_contour > size_t(0)) + out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of cut contour", "%1$d connectors are out of cut contour", m_info_stats.outside_cut_contour), + m_info_stats.outside_cut_contour); + if (m_info_stats.outside_bb > size_t(0)) + out += "\n - " + format_wxstr(_L_PLURAL("%1$d connector is out of object", "%1$d connectors are out of object", m_info_stats.outside_bb), + m_info_stats.outside_bb); + if (m_info_stats.is_overlap) + out += "\n - " + _L("Some connectors are overlapped"); + m_imgui->text(out); + } if (!m_keep_upper && !m_keep_lower) m_imgui->text(wxString(ImGui::WarningMarkerSmall) + _L("Invalid state. \nNo one part is selected for keep after cut")); } @@ -1848,24 +1863,30 @@ Transform3d GLGizmoCut3D::get_volume_transformation(const ModelVolume* volume) c bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) { // check if connector pos is out of clipping plane - if (m_c->object_clipper() && !m_c->object_clipper()->containes(cur_pos)) + if (m_c->object_clipper() && !m_c->object_clipper()->is_projection_inside_cut(cur_pos)) { + m_info_stats.outside_cut_contour++; return true; + } const CutConnector& cur_connector = connectors[idx]; const Transform3d matrix = translation_transform(cur_pos) * m_rotation_m * scale_transform(Vec3f(cur_connector.radius, cur_connector.radius, cur_connector.height).cast()); const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix); - if (!bounding_box().contains(cur_tbb)) + if (!bounding_box().contains(cur_tbb)) { + m_info_stats.outside_bb++; return true; + } for (size_t i = 0; i < connectors.size(); ++i) { if (i == idx) continue; const CutConnector& connector = connectors[i]; - if ((connector.pos - cur_connector.pos).norm() < double(connector.radius + cur_connector.radius)) + if ((connector.pos - cur_connector.pos).norm() < double(connector.radius + cur_connector.radius)) { + m_info_stats.is_overlap = true; return true; + } } return false; @@ -1899,6 +1920,7 @@ void GLGizmoCut3D::render_connectors() const Vec3d& normal = cp && cp->is_active() ? cp->get_normal() : m_clp_normal; m_has_invalid_connector = false; + m_info_stats.invalidate(); for (size_t i = 0; i < connectors.size(); ++i) { const CutConnector& connector = connectors[i]; @@ -1970,6 +1992,8 @@ void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowel void GLGizmoCut3D::perform_cut(const Selection& selection) { + if (!can_perform_cut()) + return; const int instance_idx = selection.get_instance_idx(); const int object_idx = selection.get_object_idx(); @@ -1985,13 +2009,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) // m_cut_z is the distance from the bed. Subtract possible SLA elevation. const double sla_shift_z = selection.get_first_volume()->get_sla_shift_z(); - const double object_cut_z = m_plane_center.z() - sla_shift_z; const Vec3d instance_offset = mo->instances[instance_idx]->get_offset(); Vec3d cut_center_offset = m_plane_center - instance_offset; cut_center_offset[Z] -= sla_shift_z; - if (0.0 < object_cut_z) { + // perform cut + { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Cut by Plane")); bool create_dowels_as_separate_object = false; @@ -2008,9 +2032,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection) only_if(m_rotate_lower, ModelObjectCutAttribute::FlipLower) | only_if(create_dowels_as_separate_object, ModelObjectCutAttribute::CreateDowels)); } - else { - // the object is SLA-elevated and the plane is under it. - } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index e33c5c897..e0fc4d3e8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -74,6 +74,19 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_old_center; + struct InvalidConnectorsStatistics + { + unsigned int outside_cut_contour; + unsigned int outside_bb; + bool is_overlap; + + void invalidate() { + outside_cut_contour = 0; + outside_bb = 0; + is_overlap = false; + } + } m_info_stats; + bool m_keep_upper{ true }; bool m_keep_lower{ true }; bool m_place_on_cut_upper{ true }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 435d8dc6e..9734f1b77 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -456,62 +456,14 @@ void ObjectClipper::render_cut() const } } -bool ObjectClipper::containes(Vec3d point) const +bool ObjectClipper::is_projection_inside_cut(const Vec3d& point) const { - if (m_clp_ratio == 0.) - return false; - const SelectionInfo* sel_info = get_pool()->selection_info(); - int sel_instance_idx = sel_info->get_active_instance(); - if (sel_instance_idx < 0) - return false; - const ModelObject* mo = sel_info->model_object(); - const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); - - size_t clipper_id = 0; - for (const ModelVolume* mv : mo->volumes) { - const Geometry::Transformation vol_trafo = mv->get_transformation(); - Geometry::Transformation trafo = inst_trafo * vol_trafo; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - auto& clipper = m_clippers[clipper_id]; - clipper->set_plane(*m_clp); - clipper->set_transformation(trafo); - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); - if (clipper->contains(point)) - return true; - - ++clipper_id; - } - return false; + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const std::unique_ptr& cl) { return cl->is_projection_inside_cut(point); }); } bool ObjectClipper::has_valid_contour() const { - if (m_clp_ratio == 0.) - return false; - const SelectionInfo* sel_info = get_pool()->selection_info(); - int sel_instance_idx = sel_info->get_active_instance(); - if (sel_instance_idx < 0) - return false; - const ModelObject* mo = sel_info->model_object(); - const Geometry::Transformation inst_trafo = mo->instances[sel_instance_idx]->get_transformation(); - - size_t clipper_id = 0; - for (const ModelVolume* mv : mo->volumes) { - const Geometry::Transformation vol_trafo = mv->get_transformation(); - Geometry::Transformation trafo = inst_trafo * vol_trafo; - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift())); - - auto& clipper = m_clippers[clipper_id]; - clipper->set_plane(*m_clp); - clipper->set_transformation(trafo); - clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD)); - if (clipper->has_valid_contour()) - return true; - - ++clipper_id; - } - return false; + return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const std::unique_ptr& cl) { return cl->has_valid_contour(); }); } void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 79483e403..f8ead27f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -266,7 +266,7 @@ public: void pass_mouse_click(const Vec3d& pt); std::vector get_disabled_contours() const; - bool containes(Vec3d point) const; + bool is_projection_inside_cut(const Vec3d& point_in) const; bool has_valid_contour() const; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index f711ecf03..5ec066f44 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -146,41 +146,23 @@ void MeshClipper::render_contour() #endif // ENABLE_LEGACY_OPENGL_REMOVAL } -bool MeshClipper::contains(Vec3d point) +bool MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const { - if (!m_result) - recalculate_triangles(); + if (!m_result || m_result->cut_islands.empty()) + return false; + Vec3d point = m_result->trafo.inverse() * point_in; + Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y())); - for (CutIsland& isl : m_result->cut_islands) { - BoundingBoxf3 bb = isl.model_expanded.get_bounding_box(); - - // instead of using of standard bb.contains(point) - // because of precision (Note, that model_expanded is pretranslate(0.003 * normal.normalized())) - constexpr double pres = 0.01; - bool ret = (point.x() > bb.min.x() || is_approx(point.x(), bb.min.x(), pres)) && (point.x() < bb.max.x() || is_approx(point.x(), bb.max.x(), pres)) - && (point.y() > bb.min.y() || is_approx(point.y(), bb.min.y(), pres)) && (point.y() < bb.max.y() || is_approx(point.y(), bb.max.y(), pres)) - && (point.z() > bb.min.z() || is_approx(point.z(), bb.min.z(), pres)) && (point.z() < bb.max.z() || is_approx(point.z(), bb.max.z(), pres)); - if (ret) { - // when we detected, that model_expanded's bb contains a point, then check if its polygon contains this point - Vec3d point_inv = m_result->trafo.inverse() * point; - Point pt = Point(scale_(point_inv.x()), scale_(point_inv.y())); - if (isl.expoly.contains(pt)) - return true; - } + for (const CutIsland& isl : m_result->cut_islands) { + if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d)) + return true; } return false; } -bool MeshClipper::has_valid_contour() +bool MeshClipper::has_valid_contour() const { - if (!m_result) - recalculate_triangles(); - - for (CutIsland& isl : m_result->cut_islands) - if (isl.model_expanded.get_bounding_box().defined) - return true; - - return false; + return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); }); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 186b74feb..9db2ed1b1 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -115,8 +115,8 @@ public: void pass_mouse_click(const Vec3d& pt); - bool contains(Vec3d point); - bool has_valid_contour(); + bool is_projection_inside_cut(const Vec3d& point) const; + bool has_valid_contour() const; private: void recalculate_triangles(); From 9b0a69e50e102b05e0fc5af32cb8a90ebd003f71 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 26 Oct 2022 15:26:35 +0200 Subject: [PATCH 92/93] CutGizmo: Fixed grabbers hovering after merge with master + Added possibility to use circle cut plane + Deleted unused code --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 100 +++++---------------------- src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 3 +- 2 files changed, 17 insertions(+), 86 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 7e3b709d6..14b31d7c5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -599,81 +599,6 @@ static wxString get_label(Vec2d vec) return str; } -bool GLGizmoCut3D::can_render_cut_plane_line(bool render_values_in_debug/* = false*/) -{ - const Camera& camera = wxGetApp().plater()->get_camera(); - - Vec3d unit_dir = m_rotation_m * Vec3d::UnitZ(); - - const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); - - const Vec2d screen_coord = world_to_ss(m_plane_center, projection_view_matrix, camera.get_viewport()); - - const Vec3d mouse_dir = m_parent.mouse_ray(Point(screen_coord.x(), screen_coord.y())).unit_vector(); - - const Vec3d camera_dir = camera.get_dir_forward(); -// double proj = unit_dir.dot(camera_dir); - const double proj = unit_dir.dot(mouse_dir); - const bool can_render = std::abs(proj) <= 0.01; // 0.25 - - if (render_values_in_debug) { - ImGui::Separator(); - - m_imgui->text("Unit dir: "); - ImGui::SameLine(m_label_width); - m_imgui->text(get_label(unit_dir)); - - m_imgui->text("Camera dir: "); - ImGui::SameLine(m_label_width); - m_imgui->text(get_label(camera_dir)); - - // m_imgui->text("screen_coord: "); - // ImGui::SameLine(m_label_width); - // m_imgui->text(get_label(screen_coord)); - - m_imgui->text("Mouse dir: "); - ImGui::SameLine(m_label_width); - m_imgui->text(get_label(mouse_dir)); - -// m_imgui->text("Unit2Camera: "); - m_imgui->text("Unit2Mouse: "); - double proj = unit_dir.dot(/*camera_dir*/mouse_dir); - ImGui::SameLine(m_label_width); - m_imgui->text_colored(can_render ? ImGuiWrapper::COL_ORANGE_LIGHT : ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE()), double_to_string(proj, 2)); - } - - return can_render; -} - -void GLGizmoCut3D::render_cut_plane_line() -{ - if (cut_line_processing()) - return; - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - - if (can_render_cut_plane_line()) { - GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat"); - if (shader) { - GLModel circle; - init_from_circle(circle, m_radius * 1.25); - - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - - shader->start_using(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("width", 0.1f); - - circle.render(); - - shader->stop_using(); - } - } -} - void GLGizmoCut3D::render_cut_plane() { if (cut_line_processing()) @@ -697,7 +622,8 @@ void GLGizmoCut3D::render_cut_plane() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (can_perform_cut()) - m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); +// m_plane.set_color({ 0.8f, 0.8f, 0.8f, 0.5f }); + m_plane.set_color({ 0.9f, 0.9f, 0.9f, 0.5f }); else m_plane.set_color({ 1.0f, 0.8f, 0.8f, 0.5f }); m_plane.render(); @@ -947,7 +873,8 @@ void GLGizmoCut3D::on_set_state() void GLGizmoCut3D::on_register_raycasters_for_picking() { assert(m_raycasters.empty()); - set_volumes_picking_state(false); + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(true); init_picking_models(); @@ -977,7 +904,8 @@ void GLGizmoCut3D::on_unregister_raycasters_for_picking() { m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); m_raycasters.clear(); - set_volumes_picking_state(true); + // the gizmo grabbers are rendered on top of the scene, so the raytraced picker should take it into account + m_parent.set_raycaster_gizmos_on_top(false); } void GLGizmoCut3D::update_raycasters_for_picking() @@ -1418,8 +1346,12 @@ void GLGizmoCut3D::init_rendering_items() if (!m_angle_arc.is_initialized() || m_angle != 0.0) init_from_angle_arc(m_angle_arc, m_angle, m_grabber_connection_len); - if (!m_plane.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) - m_plane.init_from(its_make_square_plane(float(m_radius))); + if (!m_plane.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { + if (m_cut_plane_as_circle) + m_plane.init_from(its_make_frustum_dowel(2. * m_radius, 0.3, 180)); + else + m_plane.init_from(its_make_square_plane(float(m_radius))); + } } void GLGizmoCut3D::render_clipper_cut() @@ -1453,8 +1385,6 @@ void GLGizmoCut3D::on_render() render_cut_line(); - render_cut_plane_line(); - m_selection_rectangle.render(m_parent); } @@ -1474,8 +1404,10 @@ void GLGizmoCut3D::render_debug_input_window() if (auto oc = m_c->object_clipper()) oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width)); - // Camera editing - can_render_cut_plane_line(true); + ImGui::Separator(); + + if (m_imgui->checkbox(_L("Render cut plane as circle"), m_cut_plane_as_circle)) + m_plane.reset(); m_imgui->end(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index e0fc4d3e8..35b6a92ce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -96,6 +96,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; + bool m_cut_plane_as_circle{ false }; float m_connector_depth_ratio{ 3.f }; float m_connector_size{ 2.5f }; @@ -251,8 +252,6 @@ private: void render_grabber_connection(const ColorRGBA& color, Transform3d view_matrix); void render_cut_plane_grabbers(); void render_cut_line(); - bool can_render_cut_plane_line(bool render_values = false); - void render_cut_plane_line(); void perform_cut(const Selection&selection); void set_center_pos(const Vec3d¢er_pos, bool force = false); bool update_bb(); From dd8234512bc60f131a365effed7dded53bf41fdc Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 26 Oct 2022 17:36:57 +0200 Subject: [PATCH 93/93] Changes in wxWidgets.cmake to support of the updated wxWidgets v3.2.0-patched + ObjectList: Deleted code, which is no needed after update of wxWidgets --- deps/wxWidgets/wxWidgets.cmake | 4 ++-- src/slic3r/GUI/GUI_ObjectList.cpp | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 29374974b..8056a01d3 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -13,8 +13,8 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for endif() prusaslicer_add_cmake_project(wxWidgets - URL https://github.com/prusa3d/wxWidgets/archive/2a0b365df947138c513a888d707d46248d78a341.zip - URL_HASH SHA256=9ab05cd5179196fad4ae702c78eaae9418e73a402cfd390f7438e469b13eb735 + URL https://github.com/prusa3d/wxWidgets/archive/34b524f8d5134a40a90d93a16360d533af2676ae.zip + URL_HASH SHA256=e76ca0dd998905c4dbb86f41f264e6e0468504dc2398f7e7e3bba8dc37de2f45 DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG CMAKE_ARGS -DwxBUILD_PRECOMP=ON diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 8c7b1db75..164679645 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2522,17 +2522,7 @@ bool ObjectList::can_merge_to_single_object() const wxPoint ObjectList::get_mouse_position_in_control() const { - wxPoint pt = wxGetMousePosition() - this->GetScreenPosition(); - -#ifdef __APPLE__ - // Workaround for OSX. From wxWidgets 3.1.6 Hittest doesn't respect to the header of wxDataViewCtrl - if (wxDataViewItem top_item = this->GetTopItem(); top_item.IsOk()) { - auto rect = this->GetItemRect(top_item, this->GetColumn(0)); - pt.y -= rect.y; - } -#endif // __APPLE__ - - return pt; + return wxGetMousePosition() - this->GetScreenPosition(); } // NO_PARAMETERS function call means that changed object index will be determine from Selection()