From f7284a6569182ae2a0cfee6dd96100a25551531c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 27 Jan 2022 17:23:10 +0100 Subject: [PATCH 001/327] 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 ff5d89f5eb..3d5d1cd481 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 ccf8732cf9..14f305c03d 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 002/327] 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 3d5d1cd481..3688b10f96 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 14f305c03d..f8d7570a43 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 dca578bd76..9869634cc2 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 bb33e0f731..0c291cbef1 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 55cbb0c308..3a144a65d1 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 003/327] 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 3688b10f96..847a15a9da 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 f8d7570a43..d1461404b3 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 ff921ea7ce..e12974b750 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 2a75df8669..9e404a95cd 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 9869634cc2..b61310d6cb 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 0c291cbef1..7eee2069c1 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 3a144a65d1..ba1419fea4 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 004/327] 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 66b6dcf609..b6da2e63d4 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 be52ebcb9a..e8a6f6e2c8 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 dd9cf0de2e..77dc3a9663 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 82a816d1b9..30cf2b31c5 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 5f6cd7a95f..6d49b276cd 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 fa0b27269a..e1bc32bbe3 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 1e49ebc8c3..48a54fbaee 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 228f5b58c3..c5c2384080 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 3787abb2f7..ac94375902 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 cc961ee8f1..59a7b34ae0 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 005/327] 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 ff5d89f5eb..e5692cc53f 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 ccf8732cf9..26bf7c56f0 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 006/327] 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 ac94375902..40b1dc3bcc 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 59a7b34ae0..f5d031d920 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 007/327] 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 e5692cc53f..fc7fe0fe93 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 26bf7c56f0..a084f346ce 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 55cbb0c308..6d122a4199 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 008/327] 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 58484c8a53..208549bc45 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 b450b6ec7b..80b128cbed 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 48a54fbaee..c4828da8b2 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 b2d543c5d7..c87a2ba45e 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 009/327] 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 208549bc45..f365323abd 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 c4828da8b2..7f5567d83b 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 010/327] 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 f2c3ef0837..856d29318f 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 67450fb116..8d44f3e7cc 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 7a1cf206ea..3d933c4702 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 f365323abd..1e4b9ea2c5 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 80b128cbed..fcf024db1c 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 e70c1111b9..5e154e48a3 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 32cd327a80..0ff3c294c9 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 baa54480c9..a168c32d1b 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 011/327] 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 856d29318f..dcb2d23386 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 e0e018a13c..4c82c9d5d0 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 1e4b9ea2c5..1d16ee17f9 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 fcf024db1c..2c6eddc261 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 e12974b750..095234e02d 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 9e404a95cd..2a75df8669 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 5e154e48a3..9a2e5d2664 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 012/327] 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 82702f212b..d503c024ea 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 08c54e2d21..3dd37ad39a 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 4da400305c..995d1f82d1 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 31f1a24c0d..1402c2e455 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 171f61ab28..125fa0730f 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 013/327] 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 3d933c4702..2e96910f48 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 d503c024ea..b188d60af2 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 3dd37ad39a..90084b0668 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 995d1f82d1..e116a6df14 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 014/327] 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 5ba119685b..0e3dd3067e 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 604339bf0d..e5cd1f79e1 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 015/327] 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 2e96910f48..c0e7e907de 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 27c374b3f8..7462e81534 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 0e3dd3067e..9c5074a45c 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 8394dba607..1fedcd22e9 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 496cdcfc79..908307125e 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 f8885b2060..8669a8fd04 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 016/327] 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 8d44f3e7cc..5e018a2129 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 c0e7e907de..dbaded841c 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 9c5074a45c..5448dd1a2d 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 1fedcd22e9..33d254c0d9 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 017/327] 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 0000000000..a7f462bb9c --- /dev/null +++ b/resources/icons/cut_.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e018a2129..dcb4c503a7 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 dbaded841c..c0b2e8e39f 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 5448dd1a2d..ea82d61e29 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 018/327] 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 ea82d61e29..511622cf2d 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 33d254c0d9..a9552e5e7b 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 019/327] 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 dcb4c503a7..00c489ac13 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 c0b2e8e39f..c8ae063ecd 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 0dffdaab0e..f98d7d4dcd 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 3f3af0261c..abf9cefb73 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 511622cf2d..411cebbb55 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 a9552e5e7b..57be3c739f 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 020/327] 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 00c489ac13..ed341dfc66 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 c8ae063ecd..b3c5b47db1 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 411cebbb55..be040e7292 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 57be3c739f..99bc66d5ef 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 021/327] 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 be040e7292..a56218860a 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 99bc66d5ef..9fc43ec2ae 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 a6008b1837..26003085ec 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 c5c2384080..226e5f7b51 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 022/327] 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 a56218860a..5ee74cc493 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 9fc43ec2ae..ca49d2e4ea 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 26003085ec..7f60892b1b 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 226e5f7b51..efd3b436e8 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 e5cd1f79e1..ca274e4295 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 dc522eb3bf..0faf07e291 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 023/327] 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 ed341dfc66..829c76fb54 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 b3c5b47db1..6fa724162d 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 1030171e7f..599c243b52 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 91add7c567..5fe2dab23d 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 7462e81534..3cb5bf3ce3 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 b9b816b7be..45072d4a50 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 6ab87150ba..089e89dfe8 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 a15c72fb8e..3a2eca2b65 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 fb1269d260..a172672eaa 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 6ab87e0253..6b61befcf2 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 373a2396d8..be7c797bed 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 b88cd7ce8f..bff535bed0 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 024/327] 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 829c76fb54..30dd64aa0a 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 6fa724162d..3afa94c756 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 599c243b52..42cd216991 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 7b3476d711..d5e7260af4 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 bff535bed0..cc577da2f7 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 a168c32d1b..39aa7cf8b9 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 025/327] 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 3cb5bf3ce3..9b2edb6606 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 45072d4a50..25614581b1 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 908307125e..619de379b6 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 8669a8fd04..ec1d801c97 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 026/327] 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 1db09956d2..45f991c4cb 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 cbea17d8eb..58b8c7183d 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 619de379b6..9c5fac89e2 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 027/327] 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 45f991c4cb..4065c20607 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 ca49d2e4ea..cb6d222efe 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 028/327] 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 d68f99bff8..f02b017d7c 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 108e30511c..871859cf0f 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 4065c20607..6fafdbf9b5 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 cb6d222efe..f8935d61fc 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 a5848013ba..41f6818940 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 00f2aee17e..00274dfb54 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 029/327] 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 6fafdbf9b5..09e59d2393 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 f8935d61fc..400018897e 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 030/327] 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 09e59d2393..7ad1d71f69 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 400018897e..6edcded0f4 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 58b8c7183d..7822a8182f 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 2a25ddc73a..621245394c 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 031/327] 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 a034787aca..8a8bb9490b 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 7ad1d71f69..da2b28dcdf 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 032/327] 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 da2b28dcdf..b83d4ffbb7 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 a1773c649b..a1694dc176 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 033/327] 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 b83d4ffbb7..5e8c07dd9f 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 ec1d801c97..f5c61a6d49 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 034/327] 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 5e8c07dd9f..820124a7ea 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 6edcded0f4..439abea8da 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 7f60892b1b..e7ba37010e 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 a1694dc176..26c7da784b 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 035/327] 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 820124a7ea..6a8a99856f 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 7822a8182f..1719794061 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 621245394c..9c8bc8e675 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 036/327] 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 8a486000fc..002cd34567 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 94daee85f5..d129ff1c2a 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 a05a4e97e9..579a27f66f 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 42afc623d4..d9e0ce377e 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 f8430e9683..801760b8cf 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 c9d8aa4fa8..396eb0764e 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 ef7687f00f..ed994be184 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 037/327] 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 dcb2d23386..9a29789d33 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 6a8a99856f..58eb19a04d 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 439abea8da..dfaef3fb23 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 e7ba37010e..c28fe63565 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 efd3b436e8..72df620cf8 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 65b292c252..ff084b0a42 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 038/327] 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 d129ff1c2a..eb0c420faf 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 0000000000..9623d32260 --- /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 bf5fd62894..4a0875d62d 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 801760b8cf..8a093f6390 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 8388aa8ef3..0000000000 --- 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 57bcb7c2c1..0000000000 --- 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 b740c316ca..0000000000 --- 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 29b8b7e736..430da0c340 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 b585798d32..844f6dea95 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 6065f22a56..9c039376d1 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 5ab4685bfe..9aa8833ff2 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 039/327] 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 e444fb03c4..0fe75d4531 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 3396c627be..54e1a31fa9 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 9a4921a84a..4b0544488a 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 7b3476d711..4adb161c24 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 0c478a97b2..cba828c294 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 ba3f6675fd..a749ad4052 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 94e9ca5f3b..76bcfdd4a6 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 496cdcfc79..4344deb242 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 ed4888a878..def48a1e49 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 53e7d637d1..db8cfc9c26 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 96151d8c7f..1cb6ec8fc6 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 8763985109..9326c92581 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 f3921919ee..8387551f2e 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 040/327] 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 0fe75d4531..abd1379419 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 844f6dea95..d2bf98b5bd 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 5af90c5f7b..28058d94bb 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 54e1a31fa9..70c985cf90 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 a77bf401d6..545213fc3c 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 2c5262d47c..37daffd9d2 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 080de997ee..b5da80e907 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 4de8381ffe..aa074f9253 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 dda50ec053..717af39ba8 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 d72e1dd328..9bccb6b630 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 42313636ec..e1c6a7ce02 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 a9812abf2a..95caa8ed3d 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 4b0544488a..0412bca214 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 4adb161c24..6a3dad5f4d 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 cba828c294..bbbc00d42b 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 b7ff8e48f4..437a526aff 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 bfedd8e1ee..1171149b6d 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 24ae01389b..a538f2b335 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 291013fe9d..97eb5f10d9 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 e5a6937f11..5d0be13082 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 975b807dcb..6be6a94fed 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 a749ad4052..bf4fe9dc70 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 3d80954bd9..bf1f67994b 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 76bcfdd4a6..43e13841c5 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 9c5ccb834e..380b402d51 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 af03a6a080..bd6c5d85a8 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 400db751aa..c202de5e2f 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 c15132fec2..0308322f7f 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 4344deb242..05d0d60ec0 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 f8885b2060..7014acccb1 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 257fe2532c..6055a8e786 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 b2983f97fb..04ee9d090b 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 cb9a48b3e2..d8bb70d3c4 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 b78a6db95d..c0a4bedf13 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 def48a1e49..93a5fe4338 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 40a0cfc28b..9fe75c1265 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 460b30126e..2a2e3bb100 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 6b5edc30e0..52e58193c7 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 49866d0661..0d78511324 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 db8cfc9c26..d4d83dfda5 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 1cb6ec8fc6..d8d6dbf19c 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 d6f6e01124..6625f71f62 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 cab9782848..ec5286abec 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 9326c92581..67890bac87 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 8387551f2e..a22622d390 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 041/327] 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 d2bf98b5bd..1476152779 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 28058d94bb..6ba9ae15b2 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 9bccb6b630..0368a2fe28 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 1171149b6d..da8f83388f 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 b9b816b7be..c838203ae3 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 c202de5e2f..2c367d62a9 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 93a5fe4338..597c30312e 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 67890bac87..c397b4b397 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 a22622d390..db05af9eb1 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 042/327] 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 4a0875d62d..96d56c0bce 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 043/327] 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 ae2832577a..76ffc0a8d6 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 044/327] 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 96d56c0bce..e2d48ce653 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 045/327] 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 0412bca214..42e325de70 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 046/327] 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 350f8cdc09..972598b737 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 23668ee999..a6a767faa9 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 995fb10339..87be7a4e2e 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 047/327] 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 3a5c89beaa..cbf1a61d4e 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 a5b2acb32d..b761deaa67 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 2c1ee7a6c2..3b5af8cd93 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 6ddb804d4e..ced04197db 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 972598b737..8cf7ab42f3 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 dfaef3fb23..76ec2f7ba4 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 9e2f9e08b7..d82fbd587d 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 46da54ffaf6c1877de0710f5fcd4a20cda1932ac Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 12:53:02 +0200 Subject: [PATCH 048/327] First implementation of SurfaceMesh --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/SurfaceMesh.hpp | 151 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/libslic3r/SurfaceMesh.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e637b1402f..396494a181 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -249,6 +249,7 @@ set(SLIC3R_SOURCES Surface.hpp SurfaceCollection.cpp SurfaceCollection.hpp + SurfaceMesh.hpp SVG.cpp SVG.hpp Technologies.hpp diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp new file mode 100644 index 0000000000..47d5ed0dcc --- /dev/null +++ b/src/libslic3r/SurfaceMesh.hpp @@ -0,0 +1,151 @@ +#ifndef slic3r_SurfaceMesh_hpp_ +#define slic3r_SurfaceMesh_hpp_ + +#include + +namespace Slic3r { + +class TriangleMesh; + + + +enum Face_index : int; + +class Halfedge_index { + friend class SurfaceMesh; + +public: + Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} + Face_index face() const { return m_face; } + bool is_invalid() const { return int(m_face) < 0; } + bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } + bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } + +private: + Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {} + + Face_index m_face; + unsigned char m_side; +}; + + + +class Vertex_index { + friend class SurfaceMesh; + +public: + Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {} + bool is_invalid() const { return int(m_face) < 0; } + bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex. + +private: + Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {} + + Face_index m_face; + unsigned char m_vertex_idx; +}; + + + +class SurfaceMesh { +public: + explicit SurfaceMesh(const indexed_triangle_set& its) + : m_its(its), + m_face_neighbors(its_face_neighbors_par(its)) + {} + SurfaceMesh(const SurfaceMesh&) = delete; + SurfaceMesh& operator=(const SurfaceMesh&) = delete; + + Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); } + Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); } + Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; } + + Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; } + Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; } + Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); } + Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); } + Halfedge_index opposite(Halfedge_index h) const { + if (h.is_invalid()) + return h; + + int face_idx = m_face_neighbors[h.m_face][h.m_side]; + Halfedge_index h_candidate = halfedge(Face_index(face_idx)); + + if (h_candidate.is_invalid()) + return Halfedge_index(); // invalid + + for (int i=0; i<3; ++i) { + if (is_same_vertex(source(h_candidate), target(h))) { + // Meshes in PrusaSlicer should be fixed enough for the following not to happen. + assert(is_same_vertex(target(h_candidate), source(h))); + return h_candidate; + } + h_candidate = next(h_candidate); + } + return Halfedge_index(); // invalid + } + + Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); } + Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); } + Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); } + Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); } + Halfedge_index halfedge(Vertex_index source, Vertex_index target) const + { + Halfedge_index hi(source.m_face, source.m_vertex_idx); + assert(! hi.is_invalid()); + + const Vertex_index orig_target = this->target(hi); + Vertex_index current_target = orig_target; + + while (! is_same_vertex(current_target, target)) { + hi = next_around_source(hi); + if (hi.is_invalid()) + break; + current_target = this->target(hi); + if (is_same_vertex(current_target, orig_target)) + return Halfedge_index(); // invalid + } + + return hi; + } + + const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; } + + size_t degree(Vertex_index v) const + { + Halfedge_index h_first = halfedge(v); + Halfedge_index h = next_around_target(h_first); + size_t degree = 2; + while (! h.is_invalid() && h != h_first) { + h = next_around_target(h); + ++degree; + } + return h.is_invalid() ? 0 : degree - 1; + } + + size_t degree(Face_index f) const { + size_t total = 0; + for (unsigned char i=0; i<3; ++i) { + size_t d = degree(Vertex_index(f, i)); + if (d == 0) + return 0; + total += d; + } + assert(total - 6 >= 0); + return total - 6; // we counted 3 halfedges from f, and one more for each neighbor + } + + bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } + + bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + + + +private: + const std::vector m_face_neighbors; + const indexed_triangle_set& m_its; +}; + +} //namespace Slic3r + +#endif // slic3r_SurfaceMesh_hpp_ From 9a163c356b713a00600ece17f76fa5805e205198 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 12:53:34 +0200 Subject: [PATCH 049/327] Added some unit tests (SurfaceMesh) --- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_surface_mesh.cpp | 122 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 tests/libslic3r/test_surface_mesh.cpp diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index cf89b2246f..3622ac2b8e 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(${_TEST_NAME}_tests test_voronoi.cpp test_optimizers.cpp test_png_io.cpp + test_surface_mesh.cpp test_timeutils.cpp test_indexed_triangle_set.cpp test_astar.cpp diff --git a/tests/libslic3r/test_surface_mesh.cpp b/tests/libslic3r/test_surface_mesh.cpp new file mode 100644 index 0000000000..34ff356679 --- /dev/null +++ b/tests/libslic3r/test_surface_mesh.cpp @@ -0,0 +1,122 @@ +#include +#include + + +#include + +using namespace Slic3r; + + +// Generate a broken cube mesh. Face 8 is inverted, face 11 is missing. +indexed_triangle_set its_make_cube_broken(double xd, double yd, double zd) +{ + auto x = float(xd), y = float(yd), z = float(zd); + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3} /*missing face*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; +} + + + +TEST_CASE("SurfaceMesh on a cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube(1., 1., 1.); + SurfaceMesh sm(cube); + const Halfedge_index hi_first = sm.halfedge(Face_index(0)); + Halfedge_index hi = hi_first; + + REQUIRE(! hi_first.is_invalid()); + + SECTION("next / prev halfedge") { + hi = sm.next(hi); + REQUIRE(hi != hi_first); + hi = sm.next(hi); + hi = sm.next(hi); + REQUIRE(hi == hi_first); + hi = sm.prev(hi); + REQUIRE(hi != hi_first); + hi = sm.prev(hi); + hi = sm.prev(hi); + REQUIRE(hi == hi_first); + } + + SECTION("next_around_target") { + // Check that we get to the same halfedge after applying next_around_target + // four times. + const Vertex_index target_vert = sm.target(hi_first); + for (int i=0; i<4;++i) { + hi = sm.next_around_target(hi); + REQUIRE((hi == hi_first) == (i == 3)); + REQUIRE(sm.is_same_vertex(sm.target(hi), target_vert)); + REQUIRE(! sm.is_border(hi)); + } + } + + SECTION("iterate around target and source") { + hi = sm.next_around_target(hi); + hi = sm.prev_around_target(hi); + hi = sm.prev_around_source(hi); + hi = sm.next_around_source(hi); + REQUIRE(hi == hi_first); + } + + SECTION("opposite") { + const Vertex_index target = sm.target(hi); + const Vertex_index source = sm.source(hi); + hi = sm.opposite(hi); + REQUIRE(sm.is_same_vertex(target, sm.source(hi))); + REQUIRE(sm.is_same_vertex(source, sm.target(hi))); + hi = sm.opposite(hi); + REQUIRE(hi == hi_first); + } + + SECTION("halfedges walk") { + for (int i=0; i<4; ++i) { + hi = sm.next(hi); + hi = sm.opposite(hi); + } + REQUIRE(hi == hi_first); + } + + SECTION("point accessor") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + hi = sm.opposite(hi); + hi = sm.prev(hi); + hi = sm.opposite(hi); + REQUIRE(hi.face() == Face_index(6)); + REQUIRE(sm.point(sm.target(hi)).isApprox(cube.vertices[7])); + } +} + + + + +TEST_CASE("SurfaceMesh on a broken cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube_broken(1., 1., 1.); + SurfaceMesh sm(cube); + + SECTION("Check inverted face") { + Halfedge_index hi = sm.halfedge(Face_index(8)); + for (int i=0; i<3; ++i) { + REQUIRE(! hi.is_invalid()); + REQUIRE(sm.is_border(hi)); + } + REQUIRE(hi == sm.halfedge(Face_index(8))); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } + + SECTION("missing face") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + for (int i=0; i<3; ++i) + hi = sm.next_around_source(hi); + hi = sm.next(hi); + REQUIRE(sm.is_border(hi)); + REQUIRE(! hi.is_invalid()); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } +} From 1e494e30af62b36c6e6a1ad1196ee1a20ee07957 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Jun 2022 12:19:47 +0200 Subject: [PATCH 050/327] SurfaceMesh testing (to be reverted later) --- src/libslic3r/TriangleMesh.cpp | 11 +- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 253 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp | 2 + 3 files changed, 149 insertions(+), 117 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 0dffdaab0e..8dc87c6fb1 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -877,13 +877,20 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor indexed_triangle_set its_make_cube(double xd, double yd, double zd) { auto x = float(xd), y = float(yd), z = float(zd); - return { + /*return { { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} }, { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - }; + };*/ + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3}, /*{4, 3, 5}*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; } indexed_triangle_set its_make_prism(float width, float length, float height) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index f854edb817..f8d6abad98 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -12,20 +12,45 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/SurfaceMesh.hpp" #include #include + + namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; + + + +// TESTING: +static Halfedge_index hi; +static bool hi_initialized = false; +static std::unique_ptr sm_ptr; +static Vertex_index src; +static Vertex_index tgt; + + + + + + GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + indexed_triangle_set a = its_make_cone(0.05, .2); + its_rotate_x(a, M_PI); + its_translate(a, stl_vertex(0., 0., .8)); + indexed_triangle_set b = its_make_cylinder(.02, 0.8); + its_merge(a, b); + arrow.init_from(a); +} bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) { @@ -39,9 +64,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; Selection &selection = m_parent.get_selection(); if (selection.is_single_full_instance()) { - // Rotate the object so the normal points downward: - selection.flattening_rotate(m_planes[m_hover_id].normal); - m_parent.do_rotate(L("Gizmo-Place on Face")); + hi = sm_ptr->halfedge(Face_index(m_hover_id)); } return true; } @@ -105,6 +128,19 @@ void GLGizmoFlatten::on_render() const Selection& selection = m_parent.get_selection(); #if ENABLE_LEGACY_OPENGL_REMOVAL + + + + if (! hi_initialized) { + const indexed_triangle_set& its = m_c->selection_info()->model_object()->volumes.front()->mesh().its; + sm_ptr.reset(new SurfaceMesh(its)); + hi = sm_ptr->halfedge(Face_index(0)); + hi_initialized = true; + } + SurfaceMesh& sm = *sm_ptr; + + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; @@ -135,8 +171,12 @@ void GLGizmoFlatten::on_render() update_planes(); for (int i = 0; i < (int)m_planes.size(); ++i) { #if ENABLE_LEGACY_OPENGL_REMOVAL + int cur_face = hi.is_invalid() ? 1000000 : sm.face(hi); + for (int i=0; i < m_planes.size(); ++i) { m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbo.render(); + if (i == cur_face) + m_planes[i].vbo.set_color(i == m_hover_id ? ColorRGBA(.5f, 0.f, 0.f, 1.f) : ColorRGBA(1.f, 0.f, 0.f, 1.f)); + m_planes[i].vbo.render(); #else glsafe(::glColor4fv(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR.data() : DEFAULT_PLANE_COLOR.data())); if (m_planes[i].vbo.has_VBOs()) @@ -146,8 +186,92 @@ void GLGizmoFlatten::on_render() #if !ENABLE_GL_SHADERS_ATTRIBUTES glsafe(::glPopMatrix()); #endif // !ENABLE_GL_SHADERS_ATTRIBUTES + } } + + + + ///////////////// + //////////////// + ////////////////// + + auto draw_arrow = [&](const Vec3d& from, const Vec3d& to) -> void { + Vec3d desired_pos = from; + Vec3d desired_dir = to - from; + double desired_len = desired_dir.norm(); + desired_dir.normalize(); + + Transform3d m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + m.translate(desired_pos); + Eigen::Quaterniond q; + Transform3d rot = Transform3d::Identity(); + rot.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), desired_dir).toRotationMatrix(); + Transform3d sc = Transform3d::Identity(); + sc.scale(desired_len); + m = m*sc*rot; + + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + arrow.render(); + }; + + m_imgui->begin(std::string("DEBUG")); + bool invalid = hi.is_invalid(); + if (invalid) { + if (m_imgui->button(std::string("HALFEDGE INVALID (Click to reset)"))) + hi = sm.halfedge(Face_index(0)); + } else { + m_imgui->text(sm.is_border(hi) ? "BORDER HALFEDGE !" : "Halfedge is not border"); + m_imgui->text((std::string("Face: ") + std::to_string(int(hi.face()))).c_str()); + m_imgui->text(std::string("Target degree:" + std::to_string(sm.degree(sm.target(hi))))); + m_imgui->text(std::string("Face degree:" + std::to_string(sm.degree(sm.face(hi))))); + } + m_imgui->disabled_begin(invalid); + if (m_imgui->button(std::string("next"))) + hi = sm.next(hi); + if (m_imgui->button(std::string("prev"))) + hi = sm.prev(hi); + if (m_imgui->button(std::string("opposite"))) + hi = sm.opposite(hi); + if (m_imgui->button(std::string("next_around_target"))) + hi = sm.next_around_target(hi); + if (m_imgui->button(std::string("prev_around_target"))) + hi = sm.prev_around_target(hi); + if (m_imgui->button(std::string("next_around_source"))) + hi = sm.next_around_source(hi); + if (m_imgui->button(std::string("prev_around_source"))) + hi = sm.prev_around_source(hi); + if (m_imgui->button(std::string("remember one"))) + src = sm.target(hi); + if (m_imgui->button(std::string("switch to halfedge"))) { + tgt = sm.target(hi); + hi = sm.halfedge(src, tgt); + } + + if (invalid) + m_imgui->disabled_end(); + m_imgui->end(); + + if (! hi.is_invalid()) { + Vec3d a = sm.point(sm.source(hi)).cast(); + Vec3d b = sm.point(sm.target(hi)).cast(); + draw_arrow(a, b); + } + + + ///////////////// + //////////////// + ////////////////// + + + + + + glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -222,11 +346,11 @@ void GLGizmoFlatten::update_planes() for (const ModelVolume* vol : mo->volumes) { if (vol->type() != ModelVolumeType::MODEL_PART) continue; - TriangleMesh vol_ch = vol->get_convex_hull(); + TriangleMesh vol_ch = vol->mesh(); //vol->get_convex_hull(); vol_ch.transform(vol->get_matrix()); ch.merge(vol_ch); } - ch = ch.convex_hull_3d(); + //ch = ch.convex_hull_3d(); m_planes.clear(); #if ENABLE_WORLD_COORDINATE const Transform3d inst_matrix = mo->instances.front()->get_matrix_no_offset(); @@ -247,47 +371,18 @@ void GLGizmoFlatten::update_planes() std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; - int facet_idx = 0; - while (1) { - // Find next unvisited triangle: - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &face_normals[facet_idx]; - m_planes.emplace_back(); - break; - } - if (facet_idx == num_of_facets) - break; // Everything was visited already - - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = face_normals[facet_idx]; - if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) { - const Vec3i face = ch.its.indices[facet_idx]; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - m_planes.back().normal = normal_ptr->cast(); + + for (size_t i=0; i()); + m_planes.back().normal = face_normals[i].cast(); Pointf3s& verts = m_planes.back().vertices; // Now we'll transform all the points into world coordinates, so that the areas, angles and distances // make real sense. verts = transform(verts, inst_matrix); - - // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway): - if (verts.size() == 3 && - ((verts[0] - verts[1]).norm() < minimal_side - || (verts[0] - verts[2]).norm() < minimal_side - || (verts[1] - verts[2]).norm() < minimal_side)) - m_planes.pop_back(); } // Let's prepare transformation of the normal vector from mesh to instance coordinates. @@ -319,84 +414,12 @@ void GLGizmoFlatten::update_planes() polygon = Slic3r::Geometry::convex_hull(polygon); polygon = transform(polygon, tr.inverse()); - // Calculate area of the polygons and discard ones that are too small - float& area = m_planes[polygon_id].area; - area = 0.f; - for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula - area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1); - area = 0.5f * std::abs(area); - - bool discard = false; - if (area < minimal_area) - discard = true; - else { - // We also check the inner angles and discard polygons with angles smaller than the following threshold - const double angle_threshold = ::cos(10.0 * (double)PI / 180.0); - - for (unsigned int i = 0; i < polygon.size(); ++i) { - const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1]; - const Vec3d& curr = polygon[i]; - const Vec3d& next = polygon[(i == polygon.size() - 1) ? 0 : i + 1]; - - if ((prec - curr).normalized().dot((next - curr).normalized()) > angle_threshold) { - discard = true; - break; - } - } - } - - if (discard) { - m_planes[polygon_id--] = std::move(m_planes.back()); - m_planes.pop_back(); - continue; - } - // We will shrink the polygon a little bit so it does not touch the object edges: Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); centroid /= (double)polygon.size(); for (auto& vertex : polygon) vertex = 0.9f*vertex + 0.1f*centroid; - // Polygon is now simple and convex, we'll round the corners to make them look nicer. - // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex - // towards their average (controlled by 'aggressivity'). This is repeated k times. - // In next iterations, the neighbours are not always taken at the middle (to increase the - // rounding effect at the corners, where we need it most). - const unsigned int k = 10; // number of iterations - const float aggressivity = 0.2f; // agressivity - const unsigned int N = polygon.size(); - std::vector> neighbours; - if (k != 0) { - Pointf3s points_out(2*k*N); // vector long enough to store the future vertices - for (unsigned int j=0; j vertices; // should be in fact local in update_planes() #if ENABLE_LEGACY_OPENGL_REMOVAL From 15418bfb76be3830b9352b4a7d29dae5d1dd1950 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 18 Jul 2022 16:11:54 +0200 Subject: [PATCH 051/327] 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 b761deaa67..a5b2acb32d 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 ced04197db..6ddb804d4e 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 8cf7ab42f3..5ea85964ca 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 76ec2f7ba4..ea053d99f3 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 052/327] 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 19b2f8f7d0..36034d9041 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 72d9b33c1a..6afa2efe8a 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 5ea85964ca..0d8eae28d0 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 ea053d99f3..dbb1e6ae01 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 053/327] 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 0d8eae28d0..5fd0b28d4f 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 dbb1e6ae01..b562396c14 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 054/327] 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 36034d9041..b18756a5e8 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 6afa2efe8a..3242dd2557 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 3b5af8cd93..23ee84a5c8 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 b3f71687b8..c89ee949ab 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 936ac99d8a..2a4b3e46ab 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 5fd0b28d4f..417cf6037e 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 b562396c14..91f8749e76 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 f0cf420a8457e1355181ba0e22efcb8f4856dcf8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 15:37:11 +0200 Subject: [PATCH 055/327] Measuring: separated another gizmo --- resources/icons/measure.svg | 13 + src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 388 ++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 75 +++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 7 files changed, 482 insertions(+) create mode 100644 resources/icons/measure.svg create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp diff --git a/resources/icons/measure.svg b/resources/icons/measure.svg new file mode 100644 index 0000000000..275c522251 --- /dev/null +++ b/resources/icons/measure.svg @@ -0,0 +1,13 @@ + + + Layer 1 + + + + + + + + + + \ No newline at end of file diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index 47d5ed0dcc..e8ada4cd80 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -138,6 +138,7 @@ public: bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 29b8b7e736..a56556baa5 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -61,6 +61,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoSimplify.hpp GUI/Gizmos/GLGizmoMmuSegmentation.cpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp + GUI/Gizmos/GLGizmoMeasure.cpp + GUI/Gizmos/GLGizmoMeasure.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLModel.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp new file mode 100644 index 0000000000..572ea75dbe --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -0,0 +1,388 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoMeasure.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" + +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" + +#include "libslic3r/Geometry/ConvexHull.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/SurfaceMesh.hpp" + +#include + +#include + +namespace Slic3r { +namespace GUI { + +static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; +static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; + +GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) +{} + + + +bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) +{ + if (mouse_event.Moving()) { + // only for sure + m_mouse_left_down = false; + return false; + } + if (mouse_event.LeftDown()) { + if (m_hover_id != -1) { + m_mouse_left_down = true; + Selection &selection = m_parent.get_selection(); + if (selection.is_single_full_instance()) { + // Rotate the object so the normal points downward: + selection.flattening_rotate(m_planes[m_hover_id].normal); + m_parent.do_rotate(L("Gizmo-Place on Face")); + } + return true; + } + + // fix: prevent restart gizmo when reselect object + // take responsibility for left up + if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; + + } else if (mouse_event.LeftUp()) { + if (m_mouse_left_down) { + // responsible for mouse left up after selecting plane + m_mouse_left_down = false; + return true; + } + } else if (mouse_event.Leaving()) { + m_mouse_left_down = false; + } + return false; +} + + + +void GLGizmoMeasure::data_changed() +{ + const Selection & selection = m_parent.get_selection(); + const ModelObject *model_object = nullptr; + if (selection.is_single_full_instance() || + selection.is_from_single_object() ) { + model_object = selection.get_model()->objects[selection.get_object_idx()]; + } + set_flattening_data(model_object); +} + + + +bool GLGizmoMeasure::on_init() +{ + // FIXME m_shortcut_key = WXK_CONTROL_F; + return true; +} + + + +void GLGizmoMeasure::on_set_state() +{ +} + + + +CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const +{ + return CommonGizmosDataID::SelectionInfo; +} + + + +std::string GLGizmoMeasure::on_get_name() const +{ + return _u8L("Measure"); +} + + + +bool GLGizmoMeasure::on_is_activable() const +{ + // 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 GLGizmoMeasure::on_render() +{ + const Selection& selection = m_parent.get_selection(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + + if (selection.is_single_full_instance()) { + const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (this->is_plane_update_necessary()) + update_planes(); + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + m_planes[i].vbo.render(); + } + } + + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + + shader->stop_using(); +} + + + + + +#if ! ENABLE_LEGACY_OPENGL_REMOVAL + #error NOT IMPLEMENTED +#endif +#if ! ENABLE_GL_SHADERS_ATTRIBUTES + #error NOT IMPLEMENTED +#endif + + + + + +void GLGizmoMeasure::on_render_for_picking() +{ + const Selection& selection = m_parent.get_selection(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); + + if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { + const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (this->is_plane_update_necessary()) + update_planes(); + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_planes[i].vbo.set_color(picking_color_component(i)); + m_planes[i].vbo.render(); + } + } + + glsafe(::glEnable(GL_CULL_FACE)); + + shader->stop_using(); +} + + + +void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +{ + if (model_object != m_old_model_object) { + m_planes.clear(); + m_planes_valid = false; + } +} + + + +void GLGizmoMeasure::update_planes() +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + TriangleMesh ch; + for (const ModelVolume* vol : mo->volumes) { + if (vol->type() != ModelVolumeType::MODEL_PART) + continue; + TriangleMesh vol_ch = vol->mesh(); + vol_ch.transform(vol->get_matrix()); + ch.merge(vol_ch); + } + m_planes.clear(); + const Transform3d& inst_matrix = mo->instances.front()->get_matrix(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal. + // This part is still performed in mesh coordinate system. + const int num_of_facets = ch.facets_count(); + std::vector face_to_plane(num_of_facets, 0); + const std::vector face_normals = its_face_normals(ch.its); + const std::vector face_neighbors = its_face_neighbors(ch.its); + std::vector facet_queue(num_of_facets, 0); + std::vector facet_visited(num_of_facets, false); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + int facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + while (1) { + // Find next unvisited triangle: + for (; facet_idx < num_of_facets; ++ facet_idx) + if (!facet_visited[facet_idx]) { + facet_queue[facet_queue_cnt ++] = facet_idx; + facet_visited[facet_idx] = true; + normal_ptr = &face_normals[facet_idx]; + face_to_plane[facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { + const Vec3i& face = ch.its.indices[facet_idx]; + for (int j=0; j<3; ++j) + m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); + + facet_visited[facet_idx] = true; + face_to_plane[facet_idx] = m_planes.size() - 1; + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + m_planes.back().normal = normal_ptr->cast(); + + Pointf3s& verts = m_planes.back().vertices; + // Now we'll transform all the points into world coordinates, so that the areas, angles and distances + // make real sense. + verts = transform(verts, inst_matrix); + } + + // Let's prepare transformation of the normal vector from mesh to instance coordinates. + Geometry::Transformation t(inst_matrix); + Vec3d scaling = t.get_scaling_factor(); + t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); + + // Now we'll go through all the polygons, transform the points into xy plane to process them: + for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { + Pointf3s& polygon = m_planes[polygon_id].vertices; + const Vec3d& normal = m_planes[polygon_id].normal; + + // transform the normal according to the instance matrix: + Vec3d normal_transformed = t.get_matrix() * normal; + + // We are going to rotate about z and y to flatten the plane + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); + polygon = transform(polygon, m); + + // Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since + // it works in fixed point representation, we will rescale the polygon to avoid overflows. + // And yes, it is a nasty thing to do. Whoever has time is free to refactor. + Vec3d bb_size = BoundingBoxf3(polygon).size(); + float sf = std::min(1./bb_size(0), 1./bb_size(1)); + Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); + polygon = transform(polygon, tr); + polygon = Slic3r::Geometry::convex_hull(polygon); + polygon = transform(polygon, tr.inverse()); + + // We will shrink the polygon a little bit so it does not touch the object edges: + Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); + centroid /= (double)polygon.size(); + for (auto& vertex : polygon) + vertex = 0.95f*vertex + 0.05f*centroid; + + // Raise a bit above the object surface to avoid flickering: + for (auto& b : polygon) + b(2) += 0.1f; + + // Transform back to 3D (and also back to mesh coordinates) + polygon = transform(polygon, inst_matrix.inverse() * m.inverse()); + } + + // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): + std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); + m_planes.resize(std::min((int)m_planes.size(), 254)); + + // Planes are finished - let's save what we calculated it from: + m_volumes_matrices.clear(); + m_volumes_types.clear(); + for (const ModelVolume* vol : mo->volumes) { + m_volumes_matrices.push_back(vol->get_matrix()); + m_volumes_types.push_back(vol->type()); + } + m_first_instance_scale = mo->instances.front()->get_scaling_factor(); + m_first_instance_mirror = mo->instances.front()->get_mirror(); + m_old_model_object = mo; + + // And finally create respective VBOs. The polygon is convex with + // the vertices in order, so triangulation is trivial. + for (auto& plane : m_planes) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(plane.vertices.size()); + init_data.reserve_indices(plane.vertices.size()); + // vertices + indices + for (size_t i = 0; i < plane.vertices.size(); ++i) { + init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); + init_data.add_index((unsigned int)i); + } + plane.vbo.init_from(std::move(init_data)); + + // FIXME: vertices should really be local, they need not + // persist now when we use VBOs + plane.vertices.clear(); + plane.vertices.shrink_to_fit(); + } + + m_planes_valid = true; +} + + + +bool GLGizmoMeasure::is_plane_update_necessary() const +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + if (m_state != On || ! mo || mo->instances.empty()) + return false; + + if (! m_planes_valid || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) + return true; + + // We want to recalculate when the scale changes - some planes could (dis)appear. + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + return true; + + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) + return true; + + return false; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp new file mode 100644 index 0000000000..9bf87a29f6 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -0,0 +1,75 @@ +#ifndef slic3r_GLGizmoMeasure_hpp_ +#define slic3r_GLGizmoMeasure_hpp_ + +#include "GLGizmoBase.hpp" +#if ENABLE_LEGACY_OPENGL_REMOVAL +#include "slic3r/GUI/GLModel.hpp" +#else +#include "slic3r/GUI/3DScene.hpp" +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + +namespace Slic3r { + +enum class ModelVolumeType : int; + + +namespace GUI { + + +class GLGizmoMeasure : public GLGizmoBase +{ +// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. + +private: + + struct PlaneData { + std::vector vertices; // should be in fact local in update_planes() + std::vector borders_facets; + GLModel vbo; + Vec3d normal; + float area; + }; + + // This holds information to decide whether recalculation is necessary: + std::vector m_volumes_matrices; + std::vector m_volumes_types; + Vec3d m_first_instance_scale; + Vec3d m_first_instance_mirror; + + std::vector m_planes; + std::vector m_face_to_plane; + bool m_mouse_left_down = false; // for detection left_up of this gizmo + bool m_planes_valid = false; + const ModelObject* m_old_model_object = nullptr; + std::vector instances_matrices; + + void update_planes(); + bool is_plane_update_necessary() const; + void set_flattening_data(const ModelObject* model_object); + +public: + GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed() override; +protected: + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_render() override; + void on_render_for_picking() override; + void on_set_state() override; + CommonGizmosDataID on_get_requirements() const override; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 39a1cba8e3..2a1ccd176b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,6 +21,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -106,6 +107,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); + m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 2e9e6bb65c..1a203621d2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -80,6 +80,7 @@ public: Seam, MmuSegmentation, Simplify, + Measure, Undefined }; From 5d8aaed18f97906481ae6bbd53d8bc175ab28e9f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 21 Jun 2022 12:47:47 +0200 Subject: [PATCH 056/327] Measuring: Initial plane detection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 144 +++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 572ea75dbe..129407764c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -127,6 +127,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); + glsafe(::glLineWidth(5.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -227,15 +228,14 @@ void GLGizmoMeasure::update_planes() // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. - const int num_of_facets = ch.facets_count(); - std::vector face_to_plane(num_of_facets, 0); + const size_t num_of_facets = ch.facets_count(); + std::vector face_to_plane(num_of_facets, size_t(-1)); const std::vector face_normals = its_face_normals(ch.its); const std::vector face_neighbors = its_face_neighbors(ch.its); std::vector facet_queue(num_of_facets, 0); - std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; - int facet_idx = 0; + size_t seed_facet_idx = 0; auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); @@ -243,16 +243,15 @@ void GLGizmoMeasure::update_planes() while (1) { // Find next unvisited triangle: - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &face_normals[facet_idx]; - face_to_plane[facet_idx] = m_planes.size(); + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + face_to_plane[seed_facet_idx] = m_planes.size(); m_planes.emplace_back(); break; } - if (facet_idx == num_of_facets) + if (seed_facet_idx == num_of_facets) break; // Everything was visited already while (facet_queue_cnt > 0) { @@ -260,70 +259,69 @@ void GLGizmoMeasure::update_planes() const stl_normal& this_normal = face_normals[facet_idx]; if (is_same_normal(this_normal, *normal_ptr)) { const Vec3i& face = ch.its.indices[facet_idx]; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - facet_visited[facet_idx] = true; face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } - m_planes.back().normal = normal_ptr->cast(); - Pointf3s& verts = m_planes.back().vertices; - // Now we'll transform all the points into world coordinates, so that the areas, angles and distances - // make real sense. - verts = transform(verts, inst_matrix); + m_planes.back().normal = normal_ptr->cast(); } + assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + SurfaceMesh sm(ch.its); + for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { + //int plane_id = 5; { + const auto& facets = m_planes[plane_id].facets; + std::vector pts; + for (int face_id=0; face_id{ sm.point(sm.source(he)).cast() }); + Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + } + } + + + // DEBUGGING: + //m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); + + + + + // Let's prepare transformation of the normal vector from mesh to instance coordinates. Geometry::Transformation t(inst_matrix); Vec3d scaling = t.get_scaling_factor(); t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); - // Now we'll go through all the polygons, transform the points into xy plane to process them: - for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { - Pointf3s& polygon = m_planes[polygon_id].vertices; - const Vec3d& normal = m_planes[polygon_id].normal; - - // transform the normal according to the instance matrix: - Vec3d normal_transformed = t.get_matrix() * normal; - - // We are going to rotate about z and y to flatten the plane - Eigen::Quaterniond q; - Transform3d m = Transform3d::Identity(); - m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); - polygon = transform(polygon, m); - - // Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since - // it works in fixed point representation, we will rescale the polygon to avoid overflows. - // And yes, it is a nasty thing to do. Whoever has time is free to refactor. - Vec3d bb_size = BoundingBoxf3(polygon).size(); - float sf = std::min(1./bb_size(0), 1./bb_size(1)); - Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); - polygon = transform(polygon, tr); - polygon = Slic3r::Geometry::convex_hull(polygon); - polygon = transform(polygon, tr.inverse()); - - // We will shrink the polygon a little bit so it does not touch the object edges: - Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); - centroid /= (double)polygon.size(); - for (auto& vertex : polygon) - vertex = 0.95f*vertex + 0.05f*centroid; - - // Raise a bit above the object surface to avoid flickering: - for (auto& b : polygon) - b(2) += 0.1f; - - // Transform back to 3D (and also back to mesh coordinates) - polygon = transform(polygon, inst_matrix.inverse() * m.inverse()); - } - - // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): - std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); - m_planes.resize(std::min((int)m_planes.size(), 254)); + // Planes are finished - let's save what we calculated it from: m_volumes_matrices.clear(); @@ -339,21 +337,23 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(plane.vertices.size()); - init_data.reserve_indices(plane.vertices.size()); - // vertices + indices - for (size_t i = 0; i < plane.vertices.size(); ++i) { - init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); - init_data.add_index((unsigned int)i); + for (auto& vertices : plane.borders) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(vertices.size()); + init_data.reserve_indices(vertices.size()); + // vertices + indices + for (size_t i = 0; i < vertices.size(); ++i) { + init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); + init_data.add_index((unsigned int)i); + } + plane.vbo.init_from(std::move(init_data)); } - plane.vbo.init_from(std::move(init_data)); // FIXME: vertices should really be local, they need not // persist now when we use VBOs - plane.vertices.clear(); - plane.vertices.shrink_to_fit(); + plane.borders.clear(); + plane.borders.shrink_to_fit(); } m_planes_valid = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9bf87a29f6..87ad73d8c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,8 +24,8 @@ class GLGizmoMeasure : public GLGizmoBase private: struct PlaneData { - std::vector vertices; // should be in fact local in update_planes() - std::vector borders_facets; + std::vector> borders; // should be in fact local in update_planes() + std::vector facets; GLModel vbo; Vec3d normal; float area; From 985b16e858621579d17c1b716ae42bd55ede7456 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 22 Jun 2022 10:34:58 +0200 Subject: [PATCH 057/327] Measuring: Simple visualization --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 62 ++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 129407764c..de3fc398b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -17,15 +17,13 @@ namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; -static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; +static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.9f }; +static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1.f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) {} - - bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { if (mouse_event.Moving()) { @@ -139,9 +137,29 @@ void GLGizmoMeasure::on_render() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); - for (int i = 0; i < (int)m_planes.size(); ++i) { - m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbo.render(); + + m_imgui->begin(std::string("DEBUG")); + if (m_imgui->button("<-")) + --m_currently_shown_plane; + ImGui::SameLine(); + if (m_imgui->button("->")) + ++m_currently_shown_plane; + m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, int(m_planes.size())-1); + m_imgui->text(std::to_string(m_currently_shown_plane)); + m_imgui->end(); + + + //for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_currently_shown_plane; + std::cout << m_hover_id << "\t" << m_currently_shown_plane << "\t" << std::endl; + if (i < m_planes.size()) { + for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { + m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + if (j == m_hover_id) + m_planes[i].vbos[j].render(); + std::cout << " * " << j; + } + std::cout <get_instance_transformation().get_matrix(); @@ -189,9 +208,13 @@ void GLGizmoMeasure::on_render_for_picking() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); - for (int i = 0; i < (int)m_planes.size(); ++i) { - m_planes[i].vbo.set_color(picking_color_component(i)); - m_planes[i].vbo.render(); + //for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_currently_shown_plane; + if (i < m_planes.size()) { + for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { + m_planes[i].vbos[j].set_color(picking_color_component(j)); + m_planes[i].vbos[j].render(); + } } } @@ -277,7 +300,7 @@ void GLGizmoMeasure::update_planes() for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; - std::vector pts; + std::vector pts; for (int face_id=0; face_id{ sm.point(sm.source(he)).cast() }); + pts.emplace_back(sm.point(sm.source(he)).cast()); Vertex_index target = sm.target(he); const Halfedge_index he_start = he; @@ -303,14 +326,20 @@ void GLGizmoMeasure::update_planes() while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) he = sm.next_around_target(he); he = sm.opposite(he); - m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + pts.emplace_back(sm.point(sm.source(he)).cast()); } while (he != he_start); + + if (pts.size() != 1) { + m_planes[plane_id].borders.emplace_back(pts); + pts.clear(); + } + } } // DEBUGGING: - //m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); + m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); @@ -337,7 +366,7 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - for (auto& vertices : plane.borders) { + for (const auto& vertices : plane.borders) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(vertices.size()); @@ -347,7 +376,8 @@ void GLGizmoMeasure::update_planes() init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); init_data.add_index((unsigned int)i); } - plane.vbo.init_from(std::move(init_data)); + plane.vbos.emplace_back(); + plane.vbos.back().init_from(std::move(init_data)); } // FIXME: vertices should really be local, they need not diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 87ad73d8c6..32b43e6f84 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,10 +23,12 @@ class GLGizmoMeasure : public GLGizmoBase private: + int m_currently_shown_plane = 0; + struct PlaneData { std::vector> borders; // should be in fact local in update_planes() std::vector facets; - GLModel vbo; + std::vector vbos; Vec3d normal; float area; }; From 9aa706c0a743d68847b3237c4027abafe0b7adee Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Jul 2022 15:51:51 +0200 Subject: [PATCH 058/327] Measuring: First steps on extracting features --- src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 204 ++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 20 ++- 3 files changed, 183 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index e8ada4cd80..a4b261ceb9 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -17,6 +17,7 @@ class Halfedge_index { public: Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} Face_index face() const { return m_face; } + unsigned char side() const { return m_side; } bool is_invalid() const { return int(m_face) < 0; } bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index de3fc398b9..6c95e70b7f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -22,7 +22,10 @@ static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1 GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + m_vbo_sphere.init_from(its_make_sphere(1., M_PI/32.)); + m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); +} bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { @@ -125,7 +128,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); - glsafe(::glLineWidth(5.f)); + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -144,22 +147,47 @@ void GLGizmoMeasure::on_render() ImGui::SameLine(); if (m_imgui->button("->")) ++m_currently_shown_plane; - m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, int(m_planes.size())-1); - m_imgui->text(std::to_string(m_currently_shown_plane)); + m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); + m_imgui->text(std::to_string(m_currently_shown_plane)); + m_imgui->checkbox(wxString("Show all"), m_show_all); m_imgui->end(); - //for (int i = 0; i < (int)m_planes.size(); ++i) { - int i = m_currently_shown_plane; - std::cout << m_hover_id << "\t" << m_currently_shown_plane << "\t" << std::endl; - if (i < m_planes.size()) { + int i = m_show_all ? 0 : m_currently_shown_plane; + for (int i = 0; i < (int)m_planes.size(); ++i) { + // Render all the borders. for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - if (j == m_hover_id) m_planes[i].vbos[j].render(); - std::cout << " * " << j; } - std::cout <::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); + view_feature_matrix *= q; + view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); + m_vbo_cylinder.render(); + } + + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + + + shader->set_uniform("view_model_matrix", view_model_matrix); + } + + if (! m_show_all) + break; } } @@ -196,7 +224,7 @@ void GLGizmoMeasure::on_render_for_picking() glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_BLEND)); - glsafe(::glLineWidth(5.f)); + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -235,6 +263,73 @@ void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) +{ + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + const double edge_threshold = 10. * (M_PI/180.); + + + + for (const std::vector& border : plane.borders) { + assert(border.size() > 1); + assert(! border.front().isApprox(border.back())); + double last_angle = 0.; + size_t first_idx = 0; + + for (size_t i=0; i edge_threshold || i == border.size() - 1) { + // Current feature ended. Save it and remember current point as beginning of the next. + bool is_line = (i == first_idx + 1); + plane.surface_features.emplace_back(SurfaceFeature{ + is_line ? SurfaceFeature::Line : SurfaceFeature::Circle, + is_line ? border[first_idx] : border[first_idx], // FIXME + border[i], + 0. // FIXME + }); + first_idx = i; + } else if (Slic3r::is_approx(angle, last_angle)) { + // possibly a segment of a circle + } else { + first_idx = i; + + } + } + last_angle = angle; + } + + // FIXME: Possibly merge the first and last feature. + + + + std::cout << "==================== " << std::endl; + } + + + for (const SurfaceFeature& f : plane.surface_features) { + std::cout << "- detected " << (f.type == SurfaceFeature::Line ? "Line" : "Circle") << std::endl; + std::cout<< f.pos << std::endl << std::endl << f.endpoint << std::endl; + std::cout << "----------------" << std::endl; + } + + + +} + + + void GLGizmoMeasure::update_planes() { const ModelObject* mo = m_c->selection_info()->model_object(); @@ -292,6 +387,7 @@ void GLGizmoMeasure::update_planes() } m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); @@ -300,40 +396,57 @@ void GLGizmoMeasure::update_planes() for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; - std::vector pts; + m_planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + for (int face_id=0; face_id()); - Vertex_index target = sm.target(he); - const Halfedge_index he_start = he; - - do { + // he is the first halfedge on the border. Now walk around and append the points. const Halfedge_index he_orig = he; - he = sm.next_around_target(he); - while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) - he = sm.next_around_target(he); - he = sm.opposite(he); - pts.emplace_back(sm.point(sm.source(he)).cast()); - } while (he != he_start); + m_planes[plane_id].borders.emplace_back(); + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; - if (pts.size() != 1) { - m_planes[plane_id].borders.emplace_back(pts); - pts.clear(); + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + m_planes[plane_id].borders.back().resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + + if (m_planes[plane_id].borders.back().size() == 1) + m_planes[plane_id].borders.pop_back(); } - } } @@ -365,8 +478,8 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. - for (auto& plane : m_planes) { - for (const auto& vertices : plane.borders) { + for (PlaneData& plane : m_planes) { + for (std::vector& vertices : plane.borders) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(vertices.size()); @@ -378,8 +491,17 @@ void GLGizmoMeasure::update_planes() } plane.vbos.emplace_back(); plane.vbos.back().init_from(std::move(init_data)); + vertices.pop_back(); // first and last are the same } + static int n=0; + std::cout << "==================== " << std::endl; + std::cout << "==================== " << std::endl; + std::cout << "==================== " << std::endl; + std::cout << "Plane num. " << n++ << std::endl; + extract_features(plane); + + // FIXME: vertices should really be local, they need not // persist now when we use VBOs plane.borders.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 32b43e6f84..3099366736 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,15 +24,33 @@ class GLGizmoMeasure : public GLGizmoBase private: int m_currently_shown_plane = 0; + bool m_show_all = false; + + GLModel m_vbo_sphere; + GLModel m_vbo_cylinder; + + struct SurfaceFeature { + enum Type { + Circle, + Line + }; + Type type; + Vec3d pos; + Vec3d endpoint; // for type == Line + double radius; // for type == Circle; + }; struct PlaneData { - std::vector> borders; // should be in fact local in update_planes() std::vector facets; + std::vector> borders; // should be in fact local in update_planes() + std::vector surface_features; std::vector vbos; Vec3d normal; float area; }; + static void extract_features(PlaneData& plane); + // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; From 9644fd4c595a382f3bd0dd1b1eb49092d123b704 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 7 Jul 2022 12:30:26 +0200 Subject: [PATCH 059/327] Measuring: Improved visualization --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 50 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 ++- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6c95e70b7f..0cb1067c39 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -149,12 +149,15 @@ void GLGizmoMeasure::on_render() ++m_currently_shown_plane; m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); m_imgui->text(std::to_string(m_currently_shown_plane)); - m_imgui->checkbox(wxString("Show all"), m_show_all); + m_imgui->checkbox(wxString("Show all"), m_show_all_planes); + m_imgui->checkbox(wxString("Show points"), m_show_points); + m_imgui->checkbox(wxString("Show edges"), m_show_edges); + m_imgui->checkbox(wxString("Show circles"), m_show_circles); m_imgui->end(); - int i = m_show_all ? 0 : m_currently_shown_plane; - for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_show_all_planes ? 0 : m_currently_shown_plane; + for (; i < (int)m_planes.size(); ++i) { // Render all the borders. for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); @@ -165,7 +168,7 @@ void GLGizmoMeasure::on_render() // Render features: for (const SurfaceFeature& feature : m_planes[i].surface_features) { Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - if (feature.type == SurfaceFeature::Line) { + if (m_show_edges && feature.type == SurfaceFeature::Line) { auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); view_feature_matrix *= q; view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); @@ -174,19 +177,21 @@ void GLGizmoMeasure::on_render() m_vbo_cylinder.render(); } - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); + if (m_show_points && feature.type == SurfaceFeature::Line || m_show_circles && feature.type == SurfaceFeature::Circle) { + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + } shader->set_uniform("view_model_matrix", view_model_matrix); } - if (! m_show_all) + if (! m_show_all_planes) break; } } @@ -268,7 +273,7 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) plane.surface_features.clear(); const Vec3d& normal = plane.normal; - const double edge_threshold = 10. * (M_PI/180.); + const double edge_threshold = 25. * (M_PI/180.); @@ -276,9 +281,10 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) assert(border.size() > 1); assert(! border.front().isApprox(border.back())); double last_angle = 0.; - size_t first_idx = 0; + int first_idx = 0; + bool circle = false; - for (size_t i=0; i edge_threshold || i == border.size() - 1) { + bool same_as_last = Slic3r::is_approx(angle, last_angle); + + if (std::abs(angle) > edge_threshold || (! same_as_last && circle) || i == border.size() - 1) { // Current feature ended. Save it and remember current point as beginning of the next. bool is_line = (i == first_idx + 1); plane.surface_features.emplace_back(SurfaceFeature{ @@ -300,11 +308,13 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) 0. // FIXME }); first_idx = i; - } else if (Slic3r::is_approx(angle, last_angle)) { + circle = false; + } else if (same_as_last && ! circle) { // possibly a segment of a circle - } else { + first_idx = std::max(i-2, 0); + circle = true; + } else if (! circle) { first_idx = i; - } } last_angle = angle; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 3099366736..9534796a71 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,7 +24,10 @@ class GLGizmoMeasure : public GLGizmoBase private: int m_currently_shown_plane = 0; - bool m_show_all = false; + bool m_show_all_planes = false; + bool m_show_points = true; + bool m_show_edges = true; + bool m_show_circles = true; GLModel m_vbo_sphere; GLModel m_vbo_cylinder; From 00eb8661c0bfdc057dad9f3fd9472bb6069c1ebe Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Jul 2022 16:37:16 +0200 Subject: [PATCH 060/327] Measuring: Improved feature detection, added circle center calculation --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 120 ++++++++++++++--------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0cb1067c39..e00d587814 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -9,6 +9,7 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SurfaceMesh.hpp" +#include "libslic3r/Geometry/Circle.hpp" #include @@ -177,7 +178,7 @@ void GLGizmoMeasure::on_render() m_vbo_cylinder.render(); } - if (m_show_points && feature.type == SurfaceFeature::Line || m_show_circles && feature.type == SurfaceFeature::Circle) { + if ((m_show_points && feature.type == SurfaceFeature::Line) || m_show_circles && feature.type == SurfaceFeature::Circle) { view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -185,6 +186,14 @@ void GLGizmoMeasure::on_render() ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : ColorRGBA(0.f, 1.f, 0.f, 1.f)); m_vbo_sphere.render(); + + /*view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.endpoint)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(1.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render();*/ } @@ -243,7 +252,7 @@ void GLGizmoMeasure::on_render_for_picking() update_planes(); //for (int i = 0; i < (int)m_planes.size(); ++i) { int i = m_currently_shown_plane; - if (i < m_planes.size()) { + if (i < int(m_planes.size())) { for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(picking_color_component(j)); m_planes[i].vbos[j].render(); @@ -268,60 +277,84 @@ void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) +{ + Vec2ds pts; + double z = 0.; + for (int i=start_idx; i<=end_idx; ++i) { + Vec3d pt_transformed = trafo * border[i]; + z = pt_transformed.z(); + pts.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? + + return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); +} + + + void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) { plane.surface_features.clear(); const Vec3d& normal = plane.normal; const double edge_threshold = 25. * (M_PI/180.); + std::vector angles; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); for (const std::vector& border : plane.borders) { assert(border.size() > 1); assert(! border.front().isApprox(border.back())); - double last_angle = 0.; - int first_idx = 0; - bool circle = false; + int start_idx = -1; + + // First calculate angles at all the vertices. + angles.clear(); for (int i=0; i edge_threshold || (! same_as_last && circle) || i == border.size() - 1) { - // Current feature ended. Save it and remember current point as beginning of the next. - bool is_line = (i == first_idx + 1); - plane.surface_features.emplace_back(SurfaceFeature{ - is_line ? SurfaceFeature::Line : SurfaceFeature::Circle, - is_line ? border[first_idx] : border[first_idx], // FIXME - border[i], - 0. // FIXME - }); - first_idx = i; - circle = false; - } else if (same_as_last && ! circle) { - // possibly a segment of a circle - first_idx = std::max(i-2, 0); + + bool circle = false; + std::vector> circles; + for (int i=1; i center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(SurfaceFeature{ + SurfaceFeature::Circle, + // border[start_idx], border[end_idx], + center_and_radius.first, center_and_radius.first, center_and_radius.second + }); + } std::cout << "==================== " << std::endl; @@ -352,7 +385,7 @@ void GLGizmoMeasure::update_planes() ch.merge(vol_ch); } m_planes.clear(); - const Transform3d& inst_matrix = mo->instances.front()->get_matrix(); + // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. @@ -403,13 +436,13 @@ void GLGizmoMeasure::update_planes() assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); SurfaceMesh sm(ch.its); - for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { + for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; m_planes[plane_id].borders.clear(); std::vector> visited(facets.size(), {false, false, false}); - for (int face_id=0; face_id()); - Vertex_index target = sm.target(he); + std::vector& last_border = m_planes[plane_id].borders.back(); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); const Halfedge_index he_start = he; Face_index fi = he.face(); @@ -446,15 +480,15 @@ void GLGizmoMeasure::update_planes() assert(face_it != facets.end()); assert(*face_it == int(fi)); if (visited[face_it - facets.begin()][he.side()] && he != he_start) { - m_planes[plane_id].borders.back().resize(1); + last_border.resize(1); break; } visited[face_it - facets.begin()][he.side()] = true; - m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + last_border.emplace_back(sm.point(sm.source(he)).cast()); } while (he != he_start); - if (m_planes[plane_id].borders.back().size() == 1) + if (last_border.size() == 1) m_planes[plane_id].borders.pop_back(); } } @@ -468,11 +502,7 @@ void GLGizmoMeasure::update_planes() - // Let's prepare transformation of the normal vector from mesh to instance coordinates. - Geometry::Transformation t(inst_matrix); - Vec3d scaling = t.get_scaling_factor(); - t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); - + // Planes are finished - let's save what we calculated it from: From fe9540130aaad4d0b5a76e6ce209fd1e49d7d4af Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 26 Jul 2022 10:12:59 +0200 Subject: [PATCH 061/327] Measuring: Separating frontend and backend --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Measure.cpp | 323 ++++++++++++++++ src/libslic3r/Measure.hpp | 98 +++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 448 ++++------------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 43 +-- 5 files changed, 499 insertions(+), 415 deletions(-) create mode 100644 src/libslic3r/Measure.cpp create mode 100644 src/libslic3r/Measure.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 396494a181..1c060243da 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -178,6 +178,8 @@ set(SLIC3R_SOURCES MultiMaterialSegmentation.hpp MeshNormals.hpp MeshNormals.cpp + Measure.hpp + Measure.cpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp new file mode 100644 index 0000000000..d5cb9c24b3 --- /dev/null +++ b/src/libslic3r/Measure.cpp @@ -0,0 +1,323 @@ +#include "Measure.hpp" + +#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/SurfaceMesh.hpp" + + + +namespace Slic3r { +namespace Measure { + + + +static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) +{ + Vec2ds pts; + double z = 0.; + for (int i=start_idx; i<=end_idx; ++i) { + Vec3d pt_transformed = trafo * border[i]; + z = pt_transformed.z(); + pts.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? + + return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); +} + + + + +class MeasuringImpl { +public: + explicit MeasuringImpl(const indexed_triangle_set& its); + struct PlaneData { + std::vector facets; + std::vector> borders; // FIXME: should be in fact local in update_planes() + std::vector> surface_features; + Vec3d normal; + float area; + }; + + const std::vector& get_features() const; + +private: + void update_planes(); + void extract_features(PlaneData& plane); + void save_features(); + + + std::vector m_planes; + std::vector m_features; + const indexed_triangle_set& m_its; +}; + + + + + + +MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) +: m_its{its} +{ + update_planes(); + + for (PlaneData& plane : m_planes) { + extract_features(plane); + + plane.borders.clear(); + plane.borders.shrink_to_fit(); + } + + save_features(); +} + + +void MeasuringImpl::update_planes() +{ + m_planes.clear(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal. + // This part is still performed in mesh coordinate system. + const size_t num_of_facets = m_its.indices.size(); + std::vector face_to_plane(num_of_facets, size_t(-1)); + const std::vector face_normals = its_face_normals(m_its); + const std::vector face_neighbors = its_face_neighbors(m_its); + std::vector facet_queue(num_of_facets, 0); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + size_t seed_facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + while (1) { + // Find next unvisited triangle: + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + face_to_plane[seed_facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (seed_facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { + const Vec3i& face = m_its.indices[facet_idx]; + + face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + + m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); + } + + assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + SurfaceMesh sm(m_its); + for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { + //int plane_id = 5; { + const auto& facets = m_planes[plane_id].facets; + m_planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + + for (int face_id=0; face_id& last_border = m_planes[plane_id].borders.back(); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + last_border.resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + last_border.emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + + if (last_border.size() == 1) + m_planes[plane_id].borders.pop_back(); + } + } + } + + m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), + [](const PlaneData& p) { return p.borders.empty(); }), + m_planes.end()); +} + + + + + + +void MeasuringImpl::extract_features(PlaneData& plane) +{ + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + const double edge_threshold = 25. * (M_PI/180.); + std::vector angles; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); + + + + for (const std::vector& border : plane.borders) { + assert(border.size() > 1); + int start_idx = -1; + + + // First calculate angles at all the vertices. + angles.clear(); + for (int i=0; i> circles; + for (int i=1; i circles[cidx].first) + i = circles[cidx++].second; + else plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[i-1], border[i]))); + } + + // FIXME Throw away / do not create edges which are parts of circles. + + // FIXME Check and maybe merge first and last circle. + + for (const auto& [start_idx, end_idx] : circles) { + std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(std::unique_ptr( + new Circle(center_and_radius.first, center_and_radius.second) + )); + } + + } +} + + + +void MeasuringImpl::save_features() +{ + m_features.clear(); + for (PlaneData& plane : m_planes) + //PlaneData& plane = m_planes[0]; + { + for (std::unique_ptr& feature : plane.surface_features) { + m_features.emplace_back(feature.get()); + } + } +} + + + +const std::vector& MeasuringImpl::get_features() const +{ + return m_features; +} + + + + + + + + + + + + + +Measuring::Measuring(const indexed_triangle_set& its) +: priv{std::make_unique(its)} +{} + +Measuring::~Measuring() {} + + +const std::vector& Measuring::get_features() const +{ + return priv->get_features(); +} + + + + + + +} // namespace Measure +} // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp new file mode 100644 index 0000000000..bbc7d9e1e9 --- /dev/null +++ b/src/libslic3r/Measure.hpp @@ -0,0 +1,98 @@ +#ifndef Slic3r_Measure_hpp_ +#define Slic3r_Measure_hpp_ + +#include + +#include "Point.hpp" + + +struct indexed_triangle_set; + + + +namespace Slic3r { +namespace Measure { + + +enum class SurfaceFeatureType { + Edge = 1 << 0, + Circle = 1 << 1, + Plane = 1 << 2 + }; + +class SurfaceFeature { +public: + virtual SurfaceFeatureType get_type() const = 0; +}; + +class Edge : public SurfaceFeature { +public: + Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } + std::pair get_edge() const { return std::make_pair(m_start, m_end); } +private: + Vec3d m_start; + Vec3d m_end; +}; + +class Circle : public SurfaceFeature { +public: + Circle(const Vec3d& center, double radius) : m_center{center}, m_radius{radius} {} + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } + Vec3d get_center() const { return m_center; } + double get_radius() const { return m_radius; } +private: + Vec3d m_center; + double m_radius; +}; + +class Plane : public SurfaceFeature { +public: + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } + +}; + + +class MeasuringImpl; + + +class Measuring { +public: + // Construct the measurement object on a given its. The its must remain + // valid and unchanged during the whole lifetime of the object. + explicit Measuring(const indexed_triangle_set& its); + ~Measuring(); + + // Return a reference to a list of all features identified on the its. + const std::vector& get_features() const; + + // Given a face_idx where the mouse cursor points, return a feature that + // should be highlighted or nullptr. + const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + + // Returns distance between two SurfaceFeatures. + static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); + + // Returns true if an x/y/z distance between features makes sense. + // If so, result contains the distances. + static bool get_distances(const SurfaceFeature* a, const SurfaceFeature* b, std::array& result); + + // Returns true if an x/y/z distance between feature and a point makes sense. + // If so, result contains the distances. + static bool get_axis_aligned_distances(const SurfaceFeature* feature, const Vec3d* pt, std::array& result); + + // Returns true if measuring angles between features makes sense. + // If so, result contains the angle in radians. + static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); + + +private: + + std::unique_ptr priv; +}; + + +} // namespace Measure +} // namespace Slic3r + +#endif // Slic3r_Measure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e00d587814..043adba976 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -6,10 +6,8 @@ #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" -#include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/SurfaceMesh.hpp" -#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/Measure.hpp" #include @@ -30,6 +28,10 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { + m_mouse_pos_x = mouse_event.GetX(); + m_mouse_pos_y = mouse_event.GetY(); + + if (mouse_event.Moving()) { // only for sure m_mouse_left_down = false; @@ -38,12 +40,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.LeftDown()) { if (m_hover_id != -1) { m_mouse_left_down = true; - Selection &selection = m_parent.get_selection(); - if (selection.is_single_full_instance()) { - // Rotate the object so the normal points downward: - selection.flattening_rotate(m_planes[m_hover_id].normal); - m_parent.do_rotate(L("Gizmo-Place on Face")); - } + return true; } @@ -94,7 +91,7 @@ void GLGizmoMeasure::on_set_state() CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const { - return CommonGizmosDataID::SelectionInfo; + return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::Raycaster)); } @@ -139,70 +136,59 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (this->is_plane_update_necessary()) - update_planes(); + + + update_if_needed(); + m_imgui->begin(std::string("DEBUG")); - if (m_imgui->button("<-")) - --m_currently_shown_plane; - ImGui::SameLine(); - if (m_imgui->button("->")) - ++m_currently_shown_plane; - m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); - m_imgui->text(std::to_string(m_currently_shown_plane)); - m_imgui->checkbox(wxString("Show all"), m_show_all_planes); - m_imgui->checkbox(wxString("Show points"), m_show_points); - m_imgui->checkbox(wxString("Show edges"), m_show_edges); - m_imgui->checkbox(wxString("Show circles"), m_show_circles); - m_imgui->end(); + + m_imgui->checkbox(wxString("Show all features"), m_show_all); + + Vec3f pos; + Vec3f normal; + size_t facet_idx; + m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), m, camera, pos, normal, nullptr, &facet_idx); + ImGui::Separator(); + m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); + m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); + m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); + m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - int i = m_show_all_planes ? 0 : m_currently_shown_plane; - for (; i < (int)m_planes.size(); ++i) { - // Render all the borders. - for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { - m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbos[j].render(); - } + + if (m_show_all) { + const std::vector features = m_measuring->get_features(); + for (const Measure::SurfaceFeature* feature : features) { + + if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { + const auto* circle = static_cast(feature); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + } - // Render features: - for (const SurfaceFeature& feature : m_planes[i].surface_features) { - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - if (m_show_edges && feature.type == SurfaceFeature::Line) { - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); + else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { + const auto* edge = static_cast(feature); + auto& [start, end] = edge->get_edge(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); + view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); m_vbo_cylinder.render(); } - if ((m_show_points && feature.type == SurfaceFeature::Line) || m_show_circles && feature.type == SurfaceFeature::Circle) { - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); - - /*view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.endpoint)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(1.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render();*/ - } - - shader->set_uniform("view_model_matrix", view_model_matrix); } - - if (! m_show_all_planes) - break; + shader->set_uniform("view_model_matrix", view_model_matrix); } + m_imgui->end(); } glsafe(::glEnable(GL_CULL_FACE)); @@ -222,290 +208,45 @@ void GLGizmoMeasure::on_render() #error NOT IMPLEMENTED #endif - - - - void GLGizmoMeasure::on_render_for_picking() { - const Selection& selection = m_parent.get_selection(); - - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader == nullptr) - return; - - shader->start_using(); - - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_BLEND)); - glsafe(::glLineWidth(2.f)); - - if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (this->is_plane_update_necessary()) - update_planes(); - //for (int i = 0; i < (int)m_planes.size(); ++i) { - int i = m_currently_shown_plane; - if (i < int(m_planes.size())) { - for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { - m_planes[i].vbos[j].set_color(picking_color_component(j)); - m_planes[i].vbos[j].render(); - } - } - } - - glsafe(::glEnable(GL_CULL_FACE)); - - shader->stop_using(); } void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) { - if (model_object != m_old_model_object) { - m_planes.clear(); - m_planes_valid = false; - } + if (model_object != m_old_model_object) + update_if_needed(); } - -static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) -{ - Vec2ds pts; - double z = 0.; - for (int i=start_idx; i<=end_idx; ++i) { - Vec3d pt_transformed = trafo * border[i]; - z = pt_transformed.z(); - pts.emplace_back(pt_transformed.x(), pt_transformed.y()); - } - - auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? - - return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); -} - - - -void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) -{ - plane.surface_features.clear(); - const Vec3d& normal = plane.normal; - - const double edge_threshold = 25. * (M_PI/180.); - std::vector angles; - - Eigen::Quaterniond q; - q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); - Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); - - - - for (const std::vector& border : plane.borders) { - assert(border.size() > 1); - assert(! border.front().isApprox(border.back())); - int start_idx = -1; - - - // First calculate angles at all the vertices. - angles.clear(); - for (int i=0; i> circles; - for (int i=1; i center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(SurfaceFeature{ - SurfaceFeature::Circle, - // border[start_idx], border[end_idx], - center_and_radius.first, center_and_radius.first, center_and_radius.second - }); - } - - - std::cout << "==================== " << std::endl; - } - - - for (const SurfaceFeature& f : plane.surface_features) { - std::cout << "- detected " << (f.type == SurfaceFeature::Line ? "Line" : "Circle") << std::endl; - std::cout<< f.pos << std::endl << std::endl << f.endpoint << std::endl; - std::cout << "----------------" << std::endl; - } - - - -} - - - -void GLGizmoMeasure::update_planes() +void GLGizmoMeasure::update_if_needed() { const ModelObject* mo = m_c->selection_info()->model_object(); - TriangleMesh ch; - for (const ModelVolume* vol : mo->volumes) { - if (vol->type() != ModelVolumeType::MODEL_PART) - continue; - TriangleMesh vol_ch = vol->mesh(); - vol_ch.transform(vol->get_matrix()); - ch.merge(vol_ch); - } - m_planes.clear(); - + if (m_state != On || ! mo || mo->instances.empty()) + return; - // Now we'll go through all the facets and append Points of facets sharing the same normal. - // This part is still performed in mesh coordinate system. - const size_t num_of_facets = ch.facets_count(); - std::vector face_to_plane(num_of_facets, size_t(-1)); - const std::vector face_normals = its_face_normals(ch.its); - const std::vector face_neighbors = its_face_neighbors(ch.its); - std::vector facet_queue(num_of_facets, 0); - int facet_queue_cnt = 0; - const stl_normal* normal_ptr = nullptr; - size_t seed_facet_idx = 0; + if (! m_measuring || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) + goto UPDATE; - auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { - return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); - }; + // We want to recalculate when the scale changes - some planes could (dis)appear. + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + goto UPDATE; - while (1) { - // Find next unvisited triangle: - for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) - if (face_to_plane[seed_facet_idx] == size_t(-1)) { - facet_queue[facet_queue_cnt ++] = seed_facet_idx; - normal_ptr = &face_normals[seed_facet_idx]; - face_to_plane[seed_facet_idx] = m_planes.size(); - m_planes.emplace_back(); - break; - } - if (seed_facet_idx == num_of_facets) - break; // Everything was visited already + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) + goto UPDATE; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = face_normals[facet_idx]; - if (is_same_normal(this_normal, *normal_ptr)) { - const Vec3i& face = ch.its.indices[facet_idx]; + return; - face_to_plane[facet_idx] = m_planes.size() - 1; - m_planes.back().facets.emplace_back(facet_idx); - for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } +UPDATE: + m_measuring.reset(new Measure::Measuring(mo->volumes.front()->mesh().its)); - m_planes.back().normal = normal_ptr->cast(); - std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); - } - - assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); - - SurfaceMesh sm(ch.its); - for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { - //int plane_id = 5; { - const auto& facets = m_planes[plane_id].facets; - m_planes[plane_id].borders.clear(); - std::vector> visited(facets.size(), {false, false, false}); - - for (int face_id=0; face_id& last_border = m_planes[plane_id].borders.back(); - last_border.emplace_back(sm.point(sm.source(he)).cast()); - //Vertex_index target = sm.target(he); - const Halfedge_index he_start = he; - - Face_index fi = he.face(); - auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); - assert(face_it != facets.end()); - assert(*face_it == int(fi)); - visited[face_it - facets.begin()][he.side()] = true; - - do { - const Halfedge_index he_orig = he; - he = sm.next_around_target(he); - while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) - he = sm.next_around_target(he); - he = sm.opposite(he); - - Face_index fi = he.face(); - auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); - assert(face_it != facets.end()); - assert(*face_it == int(fi)); - if (visited[face_it - facets.begin()][he.side()] && he != he_start) { - last_border.resize(1); - break; - } - visited[face_it - facets.begin()][he.side()] = true; - - last_border.emplace_back(sm.point(sm.source(he)).cast()); - } while (he != he_start); - - if (last_border.size() == 1) - m_planes[plane_id].borders.pop_back(); - } - } - } - - - // DEBUGGING: - m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); - - - - - - - - - // Planes are finished - let's save what we calculated it from: + // Let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); for (const ModelVolume* vol : mo->volumes) { @@ -515,65 +256,6 @@ void GLGizmoMeasure::update_planes() m_first_instance_scale = mo->instances.front()->get_scaling_factor(); m_first_instance_mirror = mo->instances.front()->get_mirror(); m_old_model_object = mo; - - // And finally create respective VBOs. The polygon is convex with - // the vertices in order, so triangulation is trivial. - for (PlaneData& plane : m_planes) { - for (std::vector& vertices : plane.borders) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(vertices.size()); - init_data.reserve_indices(vertices.size()); - // vertices + indices - for (size_t i = 0; i < vertices.size(); ++i) { - init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); - init_data.add_index((unsigned int)i); - } - plane.vbos.emplace_back(); - plane.vbos.back().init_from(std::move(init_data)); - vertices.pop_back(); // first and last are the same - } - - static int n=0; - std::cout << "==================== " << std::endl; - std::cout << "==================== " << std::endl; - std::cout << "==================== " << std::endl; - std::cout << "Plane num. " << n++ << std::endl; - extract_features(plane); - - - // FIXME: vertices should really be local, they need not - // persist now when we use VBOs - plane.borders.clear(); - plane.borders.shrink_to_fit(); - } - - m_planes_valid = true; -} - - - -bool GLGizmoMeasure::is_plane_update_necessary() const -{ - const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state != On || ! mo || mo->instances.empty()) - return false; - - if (! m_planes_valid || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) - return true; - - // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) - return true; - - for (unsigned int i=0; i < mo->volumes.size(); ++i) - if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || mo->volumes[i]->type() != m_volumes_types[i]) - return true; - - return false; } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9534796a71..2781d2b353 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -9,10 +9,15 @@ #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#include + + namespace Slic3r { enum class ModelVolumeType : int; +namespace Measure { class Measuring; } + namespace GUI { @@ -22,53 +27,27 @@ class GLGizmoMeasure : public GLGizmoBase // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. private: - - int m_currently_shown_plane = 0; - bool m_show_all_planes = false; - bool m_show_points = true; - bool m_show_edges = true; - bool m_show_circles = true; + std::unique_ptr m_measuring; GLModel m_vbo_sphere; GLModel m_vbo_cylinder; - struct SurfaceFeature { - enum Type { - Circle, - Line - }; - Type type; - Vec3d pos; - Vec3d endpoint; // for type == Line - double radius; // for type == Circle; - }; - - struct PlaneData { - std::vector facets; - std::vector> borders; // should be in fact local in update_planes() - std::vector surface_features; - std::vector vbos; - Vec3d normal; - float area; - }; - - static void extract_features(PlaneData& plane); - // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; Vec3d m_first_instance_scale; Vec3d m_first_instance_mirror; - std::vector m_planes; - std::vector m_face_to_plane; bool m_mouse_left_down = false; // for detection left_up of this gizmo bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; std::vector instances_matrices; - void update_planes(); - bool is_plane_update_necessary() const; + int m_mouse_pos_x; + int m_mouse_pos_y; + bool m_show_all = true; + + void update_if_needed(); void set_flattening_data(const ModelObject* model_object); public: From c8e9622ab21e186f24e580a596d05ecc78e1905c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 09:58:21 +0200 Subject: [PATCH 062/327] Measuring: further separating frontend and backend --- src/libslic3r/Measure.cpp | 236 +++++++++++++++-------- src/libslic3r/Measure.hpp | 24 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 91 ++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 4 files changed, 233 insertions(+), 122 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index d5cb9c24b3..a8ee3d3265 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -39,16 +39,19 @@ public: float area; }; - const std::vector& get_features() const; + const std::vector& get_features() const; + const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + const std::vector> get_planes_triangle_indices() const; private: void update_planes(); - void extract_features(PlaneData& plane); + void extract_features(); void save_features(); std::vector m_planes; - std::vector m_features; + std::vector m_face_to_plane; + std::vector m_features; const indexed_triangle_set& m_its; }; @@ -61,14 +64,7 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) : m_its{its} { update_planes(); - - for (PlaneData& plane : m_planes) { - extract_features(plane); - - plane.borders.clear(); - plane.borders.shrink_to_fit(); - } - + extract_features(); save_features(); } @@ -80,7 +76,7 @@ void MeasuringImpl::update_planes() // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. const size_t num_of_facets = m_its.indices.size(); - std::vector face_to_plane(num_of_facets, size_t(-1)); + m_face_to_plane.resize(num_of_facets, size_t(-1)); const std::vector face_normals = its_face_normals(m_its); const std::vector face_neighbors = its_face_neighbors(m_its); std::vector facet_queue(num_of_facets, 0); @@ -95,10 +91,10 @@ void MeasuringImpl::update_planes() while (1) { // Find next unvisited triangle: for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) - if (face_to_plane[seed_facet_idx] == size_t(-1)) { + if (m_face_to_plane[seed_facet_idx] == size_t(-1)) { facet_queue[facet_queue_cnt ++] = seed_facet_idx; normal_ptr = &face_normals[seed_facet_idx]; - face_to_plane[seed_facet_idx] = m_planes.size(); + m_face_to_plane[seed_facet_idx] = m_planes.size(); m_planes.emplace_back(); break; } @@ -111,10 +107,10 @@ void MeasuringImpl::update_planes() if (is_same_normal(this_normal, *normal_ptr)) { const Vec3i& face = m_its.indices[facet_idx]; - face_to_plane[facet_idx] = m_planes.size() - 1; + m_face_to_plane[facet_idx] = m_planes.size() - 1; m_planes.back().facets.emplace_back(facet_idx); for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && m_face_to_plane[neighbor_idx] == size_t(-1)) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } @@ -123,7 +119,7 @@ void MeasuringImpl::update_planes() std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } - assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); SurfaceMesh sm(m_its); for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { @@ -133,9 +129,9 @@ void MeasuringImpl::update_planes() std::vector> visited(facets.size(), {false, false, false}); for (int face_id=0; face_id angles; - Eigen::Quaterniond q; - q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); - Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); - - - for (const std::vector& border : plane.borders) { - assert(border.size() > 1); - int start_idx = -1; + for (int i=0; i& border : plane.borders) { + assert(border.size() > 1); + int start_idx = -1; + + // First calculate angles at all the vertices. + angles.clear(); + for (int i=0; i> circles; - for (int i=1; i> circles; + for (int i=1; i circles[cidx].first) + i = circles[cidx++].second; + else plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[i-1], border[i]))); + } + + // FIXME Throw away / do not create edges which are parts of circles or + // which lead to circle points (unless they belong to the same plane.) + + // FIXME Check and merge first and last circle if needed. + + // Now create the circle-typed surface features. + for (const auto& [start_idx, end_idx] : circles) { + std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(std::unique_ptr( + new Circle(center_and_radius.first, center_and_radius.second))); + } + } - // We have the circles. Now go around again and pick edges. - int cidx = 0; // index of next circle in the way - for (int i=1; i circles[cidx].first) - i = circles[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); - } - - // FIXME Throw away / do not create edges which are parts of circles. - - // FIXME Check and maybe merge first and last circle. - - for (const auto& [start_idx, end_idx] : circles) { - std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second) - )); - } + // The last surface feature is the plane itself. + plane.surface_features.emplace_back(std::unique_ptr( + new Plane(i))); + plane.borders.clear(); + plane.borders.shrink_to_fit(); } } @@ -277,7 +284,7 @@ void MeasuringImpl::save_features() for (PlaneData& plane : m_planes) //PlaneData& plane = m_planes[0]; { - for (std::unique_ptr& feature : plane.surface_features) { + for (const std::unique_ptr& feature : plane.surface_features) { m_features.emplace_back(feature.get()); } } @@ -285,13 +292,51 @@ void MeasuringImpl::save_features() -const std::vector& MeasuringImpl::get_features() const +const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const +{ + if (face_idx >= m_face_to_plane.size()) + return nullptr; + + const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; + + const SurfaceFeature* closest_feature = nullptr; + double min_dist = std::numeric_limits::max(); + + for (const std::unique_ptr& feature : plane.surface_features) { + double dist = Measuring::get_distance(feature.get(), &point); + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = feature.get(); + } + } + + if (closest_feature) + return closest_feature; + + // Nothing detected, return the plane as a whole. + assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); + return plane.surface_features.back().get(); +} + + + +const std::vector& MeasuringImpl::get_features() const { return m_features; } +const std::vector> MeasuringImpl::get_planes_triangle_indices() const +{ + std::vector> out; + for (const PlaneData& plane : m_planes) + out.emplace_back(plane.facets); + return out; +} + + + @@ -309,12 +354,39 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +const std::vector& Measuring::get_features() const { return priv->get_features(); } +const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +{ + return priv->get_feature(face_idx, point); +} + + + +const std::vector> Measuring::get_planes_triangle_indices() const +{ + return priv->get_planes_triangle_indices(); +} + + + +double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) +{ + if (feature->get_type() == SurfaceFeatureType::Edge) { + const Edge* edge = static_cast(feature); + const auto& [s,e] = edge->get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + return line.distance(*pt); + } + + return std::numeric_limits::max(); +} + + diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index bbc7d9e1e9..1360b47ff7 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -48,8 +48,12 @@ private: class Plane : public SurfaceFeature { public: + Plane(int idx) : m_idx(idx) {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } + int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices +private: + int m_idx; }; @@ -64,30 +68,30 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - const std::vector& get_features() const; + [[deprecated]]const std::vector& get_features() const; // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted or nullptr. const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // Returns a list of triangle indices for each identified plane. Each + // Plane object contains an index into this vector. + const std::vector> get_planes_triangle_indices() const; + + + // Returns distance between two SurfaceFeatures. static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - // Returns true if an x/y/z distance between features makes sense. - // If so, result contains the distances. - static bool get_distances(const SurfaceFeature* a, const SurfaceFeature* b, std::array& result); - - // Returns true if an x/y/z distance between feature and a point makes sense. - // If so, result contains the distances. - static bool get_axis_aligned_distances(const SurfaceFeature* feature, const Vec3d* pt, std::array& result); + // Returns distance between a SurfaceFeature and a point. + static double get_distance(const SurfaceFeature* a, const Vec3d* pt); // Returns true if measuring angles between features makes sense. // If so, result contains the angle in radians. static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); -private: - +private: std::unique_ptr priv; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 043adba976..a8b257a937 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -144,6 +144,7 @@ void GLGizmoMeasure::on_render() m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); + m_imgui->checkbox(wxString("Show all planes"), m_show_planes); Vec3f pos; Vec3f normal; @@ -157,37 +158,51 @@ void GLGizmoMeasure::on_render() - if (m_show_all) { - const std::vector features = m_measuring->get_features(); - for (const Measure::SurfaceFeature* feature : features) { + std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + if (m_show_all) { + features = m_measuring->get_features(); + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature* f) { + return f->get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); + } + + + for (const Measure::SurfaceFeature* feature : features) { + if (! feature) + continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); - } - - - else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); - m_vbo_cylinder.render(); - } - - + if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { + const auto* circle = static_cast(feature); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); } - shader->set_uniform("view_model_matrix", view_model_matrix); + else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { + const auto* edge = static_cast(feature); + auto& [start, end] = edge->get_edge(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + view_feature_matrix *= q; + view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_cylinder.render(); + } + else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { + const auto* plane = static_cast(feature); + assert(plane->get_plane_idx() < m_plane_models.size()); + m_plane_models[plane->get_plane_idx()]->render(); + } } + shader->set_uniform("view_model_matrix", view_model_matrix); + if (m_show_planes) + for (const auto& glmodel : m_plane_models) + glmodel->render(); + m_imgui->end(); } @@ -244,7 +259,25 @@ void GLGizmoMeasure::update_if_needed() return; UPDATE: - m_measuring.reset(new Measure::Measuring(mo->volumes.front()->mesh().its)); + const indexed_triangle_set& its = mo->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + m_plane_models.clear(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + for (const std::vector& triangle_indices : planes_triangles) { + m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); + int i = 0; + for (int idx : triangle_indices) { + init_data.add_vertex(its.vertices[its.indices[idx][0]]); + init_data.add_vertex(its.vertices[its.indices[idx][1]]); + init_data.add_vertex(its.vertices[its.indices[idx][2]]); + init_data.add_triangle(i, i+1, i+2); + i+=3; + } + m_plane_models.back()->init_from(std::move(init_data)); + } // Let's save what we calculated it from: m_volumes_matrices.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 2781d2b353..f74b82fa46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -45,7 +45,9 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; - bool m_show_all = true; + bool m_show_all = false; + bool m_show_planes = false; + std::vector> m_plane_models; void update_if_needed(); void set_flattening_data(const ModelObject* model_object); From e990254d52db00ad84d033586d7231a22d899a13 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 26 Jul 2022 16:31:01 +0200 Subject: [PATCH 063/327] 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 b18756a5e8..79273bbbe7 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 3242dd2557..a82c081369 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 417cf6037e..21917b1d66 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 91f8749e76..fecb3e846c 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 f68e7526b215428315d8fc269fe78cce80bb8303 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 11:45:42 +0200 Subject: [PATCH 064/327] Measuring: added getters for circle visualization --- src/libslic3r/Measure.cpp | 11 ++++++++++- src/libslic3r/Measure.hpp | 8 +++++++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 22 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index a8ee3d3265..724f4ab806 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -262,7 +262,7 @@ void MeasuringImpl::extract_features() for (const auto& [start_idx, end_idx] : circles) { std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second))); + new Circle(center_and_radius.first, center_and_radius.second, plane.normal))); } } @@ -382,6 +382,15 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) Eigen::ParametrizedLine line(s, (e-s).normalized()); return line.distance(*pt); } + else if (feature->get_type() == SurfaceFeatureType::Circle) { + const Circle* circle = static_cast(feature); + // Find a plane containing normal, center and the point. + const Vec3d& c = circle->get_center(); + const Vec3d& n = circle->get_normal(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(*pt); + return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + } return std::numeric_limits::max(); } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1360b47ff7..0455291bfc 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -28,22 +28,28 @@ public: class Edge : public SurfaceFeature { public: Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} + Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, + m_pin{std::unique_ptr(new Vec3d(pin))} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } std::pair get_edge() const { return std::make_pair(m_start, m_end); } private: Vec3d m_start; Vec3d m_end; + std::unique_ptr m_pin; }; class Circle : public SurfaceFeature { public: - Circle(const Vec3d& center, double radius) : m_center{center}, m_radius{radius} {} + Circle(const Vec3d& center, double radius, const Vec3d& normal) + : m_center{center}, m_radius{radius}, m_normal{normal} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } Vec3d get_center() const { return m_center; } double get_radius() const { return m_radius; } + Vec3d get_normal() const { return m_normal; } private: Vec3d m_center; double m_radius; + Vec3d m_normal; }; class Plane : public SurfaceFeature { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a8b257a937..44dd44cc55 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -174,12 +174,28 @@ void GLGizmoMeasure::on_render() if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { const auto* circle = static_cast(feature); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + const Vec3d& c = circle->get_center(); + const Vec3d& n = circle->get_normal(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_sphere.render(); + + // Now draw the circle itself - let's take a funny shortcut: + Vec3d rad = n.cross(Vec3d::UnitX()); + if (rad.squaredNorm() < 0.1) + rad = n.cross(Vec3d::UnitY()); + rad *= circle->get_radius() * rad.norm(); + const int N = 20; + for (int i=0; iset_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.render(); + } } else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { const auto* edge = static_cast(feature); From 27f7a8da0f12ec344c646ae82575d2b472099a58 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 27 Jul 2022 13:53:54 +0200 Subject: [PATCH 065/327] 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 0000000000..4ee221a44a --- /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 0000000000..32d7f99595 --- /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 9a29789d33..f9fdf575b9 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 21917b1d66..71a8bd5ba6 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 fecb3e846c..170928a07f 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 09276ef57f..070417520b 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 8c27db56e8..8b4befa773 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 066/327] 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 79273bbbe7..d62f561056 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 a82c081369..d056368bd8 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 42e325de70..e4e5fffcfe 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 71a8bd5ba6..490f67a604 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 31baf5859bb85df8f977ce173d5e9f30b7544c75 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 15:36:21 +0200 Subject: [PATCH 067/327] Measuring: Add detection of polygons and their centers --- src/libslic3r/Measure.cpp | 72 ++++++++++++++++++------ src/libslic3r/Measure.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 8 +++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 724f4ab806..056178bc4e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -192,10 +192,11 @@ void MeasuringImpl::update_planes() void MeasuringImpl::extract_features() { - - - const double edge_threshold = 25. * (M_PI/180.); + auto N_to_angle = [](double N) -> double { return 2.*M_PI / N; }; + constexpr double polygon_upper_threshold = N_to_angle(4.5); + constexpr double polygon_lower_threshold = N_to_angle(8.5); std::vector angles; + std::vector lengths; for (int i=0; i M_PI) + angle = 2*M_PI - angle; + angles.push_back(angle); + lengths.push_back(v2.squaredNorm()); } assert(border.size() == angles.size()); + assert(border.size() == lengths.size()); bool circle = false; - std::vector> circles; + std::vector> circles; + std::vector> circles_idxs; for (int i=1; i( + new Circle(center, radius, plane.normal))); circle = false; } } } + // Some of the "circles" may actually be polygons. We want them detected as + // edges, but also to remember the center and save it into those edges. + // We will add all such edges manually and delete the detected circles, + // leaving it in circles_idxs so they are not picked again: + assert(circles.size() == circles_idxs.size()); + for (int i=circles.size()-1; i>=0; --i) { + assert(circles_idxs[i].first + 1 < angles.size() - 1); // Check that this is internal point of the circle, not the first, not the last. + double angle = angles[circles_idxs[i].first + 1]; + if (angle > polygon_lower_threshold) { + if (angle < polygon_upper_threshold) { + const Vec3d center = static_cast(circles[i].get())->get_center(); + for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) + plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[j-1], border[j], center))); + } else { + // This will be handled just like a regular edge. + circles_idxs.erase(circles_idxs.begin() + i); + } + circles.erase(circles.begin() + i); + } + } + + + + + + // We have the circles. Now go around again and pick edges. int cidx = 0; // index of next circle in the way for (int i=1; i circles[cidx].first) - i = circles[cidx++].second; + if (cidx < circles_idxs.size() && i > circles_idxs[cidx].first) + i = circles_idxs[cidx++].second; else plane.surface_features.emplace_back(std::unique_ptr( new Edge(border[i-1], border[i]))); } @@ -258,13 +297,10 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. - // Now create the circle-typed surface features. - for (const auto& [start_idx, end_idx] : circles) { - std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second, plane.normal))); - } - + // Now move the circles into the feature list. + assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), + std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 0455291bfc..1db35d9fcd 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -32,6 +32,7 @@ public: m_pin{std::unique_ptr(new Vec3d(pin))} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } std::pair get_edge() const { return std::make_pair(m_start, m_end); } + const Vec3d* get_point_of_interest() const { return m_pin.get(); } private: Vec3d m_start; Vec3d m_end; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 44dd44cc55..1869983015 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -207,6 +207,14 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); + if (edge->get_point_of_interest()) { + Vec3d pin = *edge->get_point_of_interest(); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.render(); + } } else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { const auto* plane = static_cast(feature); From 31800bb85dccd3dc3161c20b297d5238ad934581 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 28 Jul 2022 14:23:51 +0200 Subject: [PATCH 068/327] 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 23ee84a5c8..87992e9583 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 c89ee949ab..a0c0cb00a4 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 d82fbd587d..72bc955d01 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 9db024b140..5f1de81519 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 4312a61225..dae862f8f2 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 fb4ff09b6e..a18b176ad3 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 070417520b..9e01382625 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 2e9e6bb65c..6e2a19bc06 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 069/327] 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 490f67a604..da06551b86 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 170928a07f..863461d501 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 070/327] 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 5c1a7c346c..5122ac3206 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 757f4e5716..31a3276c13 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 5fc43462a1..3987081efc 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 bd38354aa4..bbf1f7652c 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 071/327] 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 5122ac3206..c3a443d0b3 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 31a3276c13..8d38bb295d 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 3987081efc..1665b85498 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 072/327] 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 d62f561056..44957cd928 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 6b444eaec4..ceb8d0b54a 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 8d38bb295d..481ceaa515 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 b35b68202d..0bb9e5f3de 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 073/327] 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 481ceaa515..c0611b46df 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 074/327] 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 44957cd928..ffa74f79cd 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 55aab5d2dc..cc69546652 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 075/327] 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 ffa74f79cd..293d31eae0 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 cc69546652..769b038fa1 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 1665b85498..f8f788138a 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 076/327] 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 f3c15c1e5d..c260959a13 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 f8f788138a..81a69e4256 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 72d7cdd5f8d73119ef04319c39a58249196bf7a7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Aug 2022 13:42:26 +0200 Subject: [PATCH 077/327] Measuring: refactoring --- src/libslic3r/Measure.cpp | 166 ++++++++++++++--------- src/libslic3r/Measure.hpp | 114 ++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 45 +++--- 3 files changed, 180 insertions(+), 145 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 056178bc4e..e59e3c04d4 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -34,24 +34,21 @@ public: struct PlaneData { std::vector facets; std::vector> borders; // FIXME: should be in fact local in update_planes() - std::vector> surface_features; + std::vector surface_features; Vec3d normal; float area; }; - const std::vector& get_features() const; - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; - const std::vector> get_planes_triangle_indices() const; + std::vector get_all_features() const; + std::optional get_feature(size_t face_idx, const Vec3d& point) const; + std::vector> get_planes_triangle_indices() const; private: void update_planes(); void extract_features(); - void save_features(); - - + std::vector m_planes; std::vector m_face_to_plane; - std::vector m_features; const indexed_triangle_set& m_its; }; @@ -65,7 +62,6 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) { update_planes(); extract_features(); - save_features(); } @@ -233,7 +229,7 @@ void MeasuringImpl::extract_features() bool circle = false; - std::vector> circles; + std::vector circles; std::vector> circles_idxs; for (int i=1; i( - new Circle(center, radius, plane.normal))); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); circle = false; } } @@ -266,10 +261,10 @@ void MeasuringImpl::extract_features() double angle = angles[circles_idxs[i].first + 1]; if (angle > polygon_lower_threshold) { if (angle < polygon_upper_threshold) { - const Vec3d center = static_cast(circles[i].get())->get_center(); + const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) - plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[j-1], border[j], center))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, + border[j-1], border[j], std::make_optional(center), 0.)); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +283,8 @@ void MeasuringImpl::extract_features() for (int i=1; i circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); + else plane.surface_features.emplace_back(SurfaceFeature( + SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); } // FIXME Throw away / do not create edges which are parts of circles or @@ -298,14 +293,16 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. // Now move the circles into the feature list. - assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) { + return f.get_type() == SurfaceFeatureType::Circle; + })); plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. - plane.surface_features.emplace_back(std::unique_ptr( - new Plane(i))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, + Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -314,56 +311,58 @@ void MeasuringImpl::extract_features() -void MeasuringImpl::save_features() +std::vector MeasuringImpl::get_all_features() const { - m_features.clear(); - for (PlaneData& plane : m_planes) + std::vector features; //PlaneData& plane = m_planes[0]; - { - for (const std::unique_ptr& feature : plane.surface_features) { - m_features.emplace_back(feature.get()); - } - } + for (const PlaneData& plane : m_planes) + for (const SurfaceFeature& feature : plane.surface_features) + features.emplace_back(feature); + return features; } -const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const + + + +std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const { if (face_idx >= m_face_to_plane.size()) - return nullptr; + return std::optional(); const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; const SurfaceFeature* closest_feature = nullptr; double min_dist = std::numeric_limits::max(); - for (const std::unique_ptr& feature : plane.surface_features) { - double dist = Measuring::get_distance(feature.get(), &point); - if (dist < 0.5 && dist < min_dist) { - min_dist = std::min(dist, min_dist); - closest_feature = feature.get(); + MeasurementResult res; + SurfaceFeature point_sf(point); + + for (const SurfaceFeature& feature : plane.surface_features) { + res = get_measurement(feature, point_sf); + if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. + double dist = *res.distance_strict; + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = &feature; + } } } if (closest_feature) - return closest_feature; + return std::make_optional(*closest_feature); // Nothing detected, return the plane as a whole. - assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); - return plane.surface_features.back().get(); + assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); + return std::make_optional(plane.surface_features.back()); } -const std::vector& MeasuringImpl::get_features() const -{ - return m_features; -} - -const std::vector> MeasuringImpl::get_planes_triangle_indices() const +std::vector> MeasuringImpl::get_planes_triangle_indices() const { std::vector> out; for (const PlaneData& plane : m_planes) @@ -390,45 +389,81 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +std::vector Measuring::get_all_features() const { - return priv->get_features(); + return priv->get_all_features(); } -const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const { return priv->get_feature(face_idx, point); } -const std::vector> Measuring::get_planes_triangle_indices() const +std::vector> Measuring::get_planes_triangle_indices() const { return priv->get_planes_triangle_indices(); } -double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + + + + + + + + + +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) { - if (feature->get_type() == SurfaceFeatureType::Edge) { - const Edge* edge = static_cast(feature); - const auto& [s,e] = edge->get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - return line.distance(*pt); - } - else if (feature->get_type() == SurfaceFeatureType::Circle) { - const Circle* circle = static_cast(feature); - // Find a plane containing normal, center and the point. - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); - Eigen::Hyperplane circle_plane(n, c); - Vec3d proj = circle_plane.projection(*pt); - return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); + + const bool swap = int(a.get_type()) > int(b.get_type()); + const SurfaceFeature& f1 = swap ? b : a; + const SurfaceFeature& f2 = swap ? a : b; + + MeasurementResult result; + if (f1.get_type() == SurfaceFeatureType::Point) { + if (f2.get_type() == SurfaceFeatureType::Point) { + Vec3d diff = (f2.get_point() - f1.get_point()); + result.distance_strict = diff.norm(); + result.distance_xyz = diff; + } else if (f2.get_type() == SurfaceFeatureType::Edge) { + const auto& [s,e] = f2.get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + // Find a plane containing normal, center and the point. + const auto& [c, radius, n] = f2.get_circle(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(f1.get_point()); + result.distance_strict = std::make_optional(std::sqrt( + std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Edge) { + if (f2.get_type() == SurfaceFeatureType::Edge) { + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Circle) { + if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Plane) { + assert(f2.get_type() == SurfaceFeatureType::Plane); + } - return std::numeric_limits::max(); + + + return result; } @@ -436,5 +471,8 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + } // namespace Measure } // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1db35d9fcd..87bed1cf17 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,7 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#include #include #include "Point.hpp" @@ -15,55 +16,50 @@ namespace Measure { enum class SurfaceFeatureType { - Edge = 1 << 0, - Circle = 1 << 1, - Plane = 1 << 2 - }; + Undef, + Point, + Edge, + Circle, + Plane +}; class SurfaceFeature { -public: - virtual SurfaceFeatureType get_type() const = 0; -}; - -class Edge : public SurfaceFeature { public: - Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} - Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, - m_pin{std::unique_ptr(new Vec3d(pin))} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } - std::pair get_edge() const { return std::make_pair(m_start, m_end); } - const Vec3d* get_point_of_interest() const { return m_pin.get(); } -private: - Vec3d m_start; - Vec3d m_end; - std::unique_ptr m_pin; -}; + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) + : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} -class Circle : public SurfaceFeature { -public: - Circle(const Vec3d& center, double radius, const Vec3d& normal) - : m_center{center}, m_radius{radius}, m_normal{normal} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } - Vec3d get_center() const { return m_center; } - double get_radius() const { return m_radius; } - Vec3d get_normal() const { return m_normal; } -private: - Vec3d m_center; - double m_radius; - Vec3d m_normal; -}; + explicit SurfaceFeature(const Vec3d& pt) + : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} -class Plane : public SurfaceFeature { -public: - Plane(int idx) : m_idx(idx) {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } - int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices + + // Get type of this feature. + SurfaceFeatureType get_type() const { return m_type; } + + // For points, return the point. + Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; } + + // For edges, return start and end. + std::pair get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); } + + // For circles, return center, radius and normal. + std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } + + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. + int get_plane_idx() const { return int(m_value); } + + // For anything, return an extra point that should also be considered a part of this. + std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } private: - int m_idx; + SurfaceFeatureType m_type = SurfaceFeatureType::Undef; + Vec3d m_pt1; + Vec3d m_pt2; + std::optional m_pt3; + double m_value; }; + class MeasuringImpl; @@ -75,34 +71,36 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - [[deprecated]]const std::vector& get_features() const; + // Use only for debugging. Expensive, do not call often. + [[deprecated]] std::vector get_all_features() const; // Given a face_idx where the mouse cursor points, return a feature that - // should be highlighted or nullptr. - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // should be highlighted (if any). + std::optional get_feature(size_t face_idx, const Vec3d& point) const; // Returns a list of triangle indices for each identified plane. Each - // Plane object contains an index into this vector. - const std::vector> get_planes_triangle_indices() const; - - - - // Returns distance between two SurfaceFeatures. - static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - - // Returns distance between a SurfaceFeature and a point. - static double get_distance(const SurfaceFeature* a, const Vec3d* pt); - - // Returns true if measuring angles between features makes sense. - // If so, result contains the angle in radians. - static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); - - + // Plane object contains an index into this vector. Expensive, do not + // call too often. + std::vector> get_planes_triangle_indices() const; + private: std::unique_ptr priv; }; + + +struct MeasurementResult { + std::optional angle; + std::optional distance_infinite; + std::optional distance_strict; + std::optional distance_xyz; +}; + +// Returns distance/angle between two SurfaceFeatures. +static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); + + } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3f12597987..6362e0053a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -158,24 +158,25 @@ void GLGizmoMeasure::on_render() - std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + std::vector features; if (m_show_all) { - features = m_measuring->get_features(); + features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature* f) { - return f->get_type() == Measure::SurfaceFeatureType::Plane; + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); + } else { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat) + features.emplace_back(*feat); } - - - for (const Measure::SurfaceFeature* feature : features) { - if (! feature) - continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); + + + for (const Measure::SurfaceFeature& feature : features) { + + if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -186,7 +187,7 @@ void GLGizmoMeasure::on_render() Vec3d rad = n.cross(Vec3d::UnitX()); if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); - rad *= circle->get_radius() * rad.norm(); + rad *= radius * rad.norm(); const int N = 20; for (int i=0; iget_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + const auto& [start, end] = feature.get_edge(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; @@ -207,8 +207,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); - if (edge->get_point_of_interest()) { - Vec3d pin = *edge->get_point_of_interest(); + if (feature.get_extra_point()) { + Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -216,10 +216,9 @@ void GLGizmoMeasure::on_render() m_vbo_sphere.render(); } } - else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { - const auto* plane = static_cast(feature); - assert(plane->get_plane_idx() < m_plane_models.size()); - m_plane_models[plane->get_plane_idx()]->render(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + assert(feature.get_plane_idx() < m_plane_models.size()); + m_plane_models[feature.get_plane_idx()]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix); From aed6ddf8f392a49179f6bfbd91d813b26ca4c636 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Aug 2022 18:50:49 +0200 Subject: [PATCH 078/327] Measuring: implemented edge endpoint detection --- src/libslic3r/Measure.cpp | 91 ++++++++++++++++++++---- src/libslic3r/Measure.hpp | 9 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 14 +++- 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index e59e3c04d4..8c7254f85b 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -9,6 +9,10 @@ namespace Slic3r { namespace Measure { +constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it +constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ... + + static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) { @@ -302,7 +306,7 @@ void MeasuringImpl::extract_features() // The last surface feature is the plane itself. plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); + plane.normal, plane.borders.front().front(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -333,25 +337,39 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; - const SurfaceFeature* closest_feature = nullptr; + size_t closest_feature_idx = size_t(-1); double min_dist = std::numeric_limits::max(); MeasurementResult res; SurfaceFeature point_sf(point); - for (const SurfaceFeature& feature : plane.surface_features) { - res = get_measurement(feature, point_sf); + for (size_t i=0; i line(s, (e-s).normalized()); - result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + double dist_inf = line.distance(f1.get_point()); + Vec3d proj = line.projection(f1.get_point()); + double len_sq = (e-s).squaredNorm(); + double dist_start_sq = (proj-s).squaredNorm(); + double dist_end_sq = (proj-e).squaredNorm(); + if (dist_start_sq < len_sq && dist_end_sq < len_sq) { + // projection falls on the line - the strict distance is the same as infinite + result.distance_strict = std::make_optional(dist_inf); + } else { // the result is the closer of the endpoints + result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + } + result.distance_infinite = std::make_optional(dist_inf); + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. const auto& [c, radius, n] = f2.get_circle(); Eigen::Hyperplane circle_plane(n, c); Vec3d proj = circle_plane.projection(f1.get_point()); - result.distance_strict = std::make_optional(std::sqrt( - std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + + (f1.get_point() - proj).squaredNorm()); + + result.distance_strict = std::make_optional(dist); + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + const auto& [idx, normal, pt] = f2.get_plane(); + Eigen::Hyperplane plane(normal, pt); + result.distance_infinite = plane.absDistance(f1.get_point()); + // TODO: result.distance_strict = } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Plane) { assert(f2.get_type() == SurfaceFeatureType::Plane); - + + const auto& [idx1, normal1, pt1] = f1.get_plane(); + const auto& [idx2, normal2, pt2] = f2.get_plane(); + double angle = 0.; + + if (! normal1.isApprox(normal2)) { + // The planes are parallel, calculate distance. + Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = plane.absDistance(pt2); + } else { + // Planes are not parallel, calculate angle. + angle = std::acos(std::abs(normal1.dot(normal2))); + } + result.angle = angle; } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 87bed1cf17..fe0b1687a1 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -31,7 +31,6 @@ public: explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} - // Get type of this feature. SurfaceFeatureType get_type() const { return m_type; } @@ -44,8 +43,8 @@ public: // For circles, return center, radius and normal. std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } - // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. - int get_plane_idx() const { return int(m_value); } + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. + std::tuple get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); } // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } @@ -84,7 +83,7 @@ public: std::vector> get_planes_triangle_indices() const; private: - std::unique_ptr priv; + std::unique_ptr priv; }; @@ -98,7 +97,7 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); } // namespace Measure diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6362e0053a..26ecfb5813 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -175,7 +175,14 @@ void GLGizmoMeasure::on_render() for (const Measure::SurfaceFeature& feature : features) { - if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + if (feature.get_type() == Measure::SurfaceFeatureType::Point) { + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.render(); + } + else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); @@ -217,8 +224,9 @@ void GLGizmoMeasure::on_render() } } else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { - assert(feature.get_plane_idx() < m_plane_models.size()); - m_plane_models[feature.get_plane_idx()]->render(); + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models.size()); + m_plane_models[idx]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix); From decbf315930f627b9ccedfad390e694fa4eb1f16 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 09:05:44 +0200 Subject: [PATCH 079/327] Partial revert of 1e494e30 --- src/libslic3r/TriangleMesh.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 8dc22f1490..da6fcf28f6 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -877,20 +877,13 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor indexed_triangle_set its_make_cube(double xd, double yd, double zd) { auto x = float(xd), y = float(yd), z = float(zd); - /*return { + return { { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} }, { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - };*/ - return { - { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, - {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, - {2, 5, 6}, {2, 5, 3}, {4, 0, 3}, /*{4, 3, 5}*/ }, - { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, - {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - }; + }; } indexed_triangle_set its_make_prism(float width, float length, float height) From 4c8b78de726501f08a7c9762181cb360c8304c37 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 09:19:10 +0200 Subject: [PATCH 080/327] Use unified color for hovering in GLGizmoMeasure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 26ecfb5813..8fc2ff8ea0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,8 +16,7 @@ namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.9f }; -static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1.f }; +static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) @@ -126,7 +125,10 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); - glsafe(::glLineWidth(2.f)); +#if ENABLE_GL_CORE_PROFILE + if (!OpenGLManager::get_gl_info().is_core_profile()) +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -179,7 +181,7 @@ void GLGizmoMeasure::on_render() Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { @@ -187,7 +189,7 @@ void GLGizmoMeasure::on_render() Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); // Now draw the circle itself - let's take a funny shortcut: @@ -212,20 +214,21 @@ void GLGizmoMeasure::on_render() view_feature_matrix *= q; view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_cylinder.set_color(HOVER_COLOR); m_vbo_cylinder.render(); if (feature.get_extra_point()) { Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } } else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models.size()); + m_plane_models[idx]->set_color(HOVER_COLOR); m_plane_models[idx]->render(); } } From a14c27b42ed11be75a1cf8916b7aeaa31e7e1f1e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 10:36:28 +0200 Subject: [PATCH 081/327] New icon for Measure Gizmo --- resources/icons/measure.svg | 106 +++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/resources/icons/measure.svg b/resources/icons/measure.svg index 275c522251..3ea137a1ed 100644 --- a/resources/icons/measure.svg +++ b/resources/icons/measure.svg @@ -1,13 +1,93 @@ - - - Layer 1 - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + From 4d9c564b9a7ca1a577ad645b1a9f095922e607ee Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 10:48:32 +0200 Subject: [PATCH 082/327] Set Measure Gizmo to be activable for single volume selections only --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 11 ++--------- src/slic3r/GUI/Selection.hpp | 1 + 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8fc2ff8ea0..712631be63 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -100,17 +100,12 @@ std::string GLGizmoMeasure::on_get_name() const return _u8L("Measure"); } - - bool GLGizmoMeasure::on_is_activable() const { - // 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(); + const Selection& selection = m_parent.get_selection(); + return selection.is_single_volume() || selection.is_single_volume_instance(); } - - void GLGizmoMeasure::on_render() { const Selection& selection = m_parent.get_selection(); @@ -139,9 +134,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - update_if_needed(); - m_imgui->begin(std::string("DEBUG")); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index cebea29e05..e118f6bca5 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -328,6 +328,7 @@ public: #if ENABLE_WORLD_COORDINATE bool is_single_volume_or_modifier() const { return is_single_volume() || is_single_modifier(); } #endif // ENABLE_WORLD_COORDINATE + bool is_single_volume_instance() const { return is_single_full_instance() && m_list.size() == 1; } bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); } // returns true if the selection contains all the given indices From 474624f89e9bc678950bc3ea400885a48526bdef Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 11:02:13 +0200 Subject: [PATCH 083/327] Removed method set_flattening_data() from GLGizmoMeasure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 ++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 712631be63..9b3d30a0be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -69,7 +69,8 @@ void GLGizmoMeasure::data_changed() selection.is_from_single_object() ) { model_object = selection.get_model()->objects[selection.get_object_idx()]; } - set_flattening_data(model_object); + if (model_object != m_old_model_object) + update_if_needed(); } @@ -247,12 +248,6 @@ void GLGizmoMeasure::on_render() #error NOT IMPLEMENTED #endif -void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) -{ - if (model_object != m_old_model_object) - update_if_needed(); -} - void GLGizmoMeasure::update_if_needed() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9094007b5d..e1460c2146 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -50,7 +50,6 @@ private: std::vector> m_plane_models; void update_if_needed(); - void set_flattening_data(const ModelObject* model_object); public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -63,6 +62,7 @@ public: bool on_mouse(const wxMouseEvent &mouse_event) override; void data_changed() override; + protected: bool on_init() override; std::string on_get_name() const override; From 9d81568bf956f6152d0c984c8d1f88d850d7c1b0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Aug 2022 08:52:02 +0200 Subject: [PATCH 084/327] Measuring: allow to select single parts of a multipart object while Gizmo Measure is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 136 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 26 +++-- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 6 +- 4 files changed, 105 insertions(+), 70 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9b3d30a0be..92fb8691ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -64,12 +64,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) void GLGizmoMeasure::data_changed() { const Selection & selection = m_parent.get_selection(); - const ModelObject *model_object = nullptr; + const ModelObject* model_object = nullptr; + const ModelVolume* model_volume = nullptr; if (selection.is_single_full_instance() || selection.is_from_single_object() ) { model_object = selection.get_model()->objects[selection.get_object_idx()]; - } - if (model_object != m_old_model_object) + model_volume = model_object->volumes[selection.get_first_volume()->volume_idx()]; + } + if (model_object != m_old_model_object || model_volume != m_old_model_volume) update_if_needed(); } @@ -109,6 +111,10 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { + // do not render if the user is panning/rotating the 3d scene + if (m_parent.is_mouse_dragging()) + return; + const Selection& selection = m_parent.get_selection(); GLShaderProgram* shader = wxGetApp().get_shader("flat"); @@ -126,33 +132,34 @@ void GLGizmoMeasure::on_render() #endif // ENABLE_GL_CORE_PROFILE glsafe(::glLineWidth(2.f)); - if (selection.is_single_full_instance()) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + if (selection.is_single_volume() || selection.is_single_volume_instance()) { + const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); update_if_needed(); - m_imgui->begin(std::string("DEBUG")); - - m_imgui->checkbox(wxString("Show all features"), m_show_all); - m_imgui->checkbox(wxString("Show all planes"), m_show_planes); - Vec3f pos; Vec3f normal; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), m, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); + +#define ENABLE_DEBUG_DIALOG 0 +#if ENABLE_DEBUG_DIALOG + m_imgui->begin(std::string("DEBUG")); + m_imgui->checkbox(wxString("Show all features"), m_show_all); + m_imgui->checkbox(wxString("Show all planes"), m_show_planes); ImGui::Separator(); m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - - + m_imgui->end(); +#endif // ENABLE_DEBUG_DIALOG std::vector features; if (m_show_all) { @@ -230,8 +237,6 @@ void GLGizmoMeasure::on_render() if (m_show_planes) for (const auto& glmodel : m_plane_models) glmodel->render(); - - m_imgui->end(); } glsafe(::glEnable(GL_CULL_FACE)); @@ -251,57 +256,70 @@ void GLGizmoMeasure::on_render() void GLGizmoMeasure::update_if_needed() { + auto do_update = [this](const ModelObject* object, const ModelVolume* volume) { + const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + m_plane_models.clear(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + for (const std::vector& triangle_indices : planes_triangles) { + m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); + int i = 0; + for (int idx : triangle_indices) { + init_data.add_vertex(its.vertices[its.indices[idx][0]]); + init_data.add_vertex(its.vertices[its.indices[idx][1]]); + init_data.add_vertex(its.vertices[its.indices[idx][2]]); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + m_plane_models.back()->init_from(std::move(init_data)); + } + + // Let's save what we calculated it from: + m_volumes_matrices.clear(); + m_volumes_types.clear(); + m_first_instance_scale = Vec3d::Ones(); + m_first_instance_mirror = Vec3d::Ones(); + if (object != nullptr) { + for (const ModelVolume* vol : object->volumes) { + m_volumes_matrices.push_back(vol->get_matrix()); + m_volumes_types.push_back(vol->type()); + } + m_first_instance_scale = object->instances.front()->get_scaling_factor(); + m_first_instance_mirror = object->instances.front()->get_mirror(); + } + m_old_model_object = object; + m_old_model_volume = volume; + }; + const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state != On || ! mo || mo->instances.empty()) + const ModelVolume* mv = m_c->selection_info()->model_volume(); + if (m_state != On || (mo == nullptr && mv == nullptr)) return; - if (! m_measuring || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) - goto UPDATE; + if (mo == nullptr) + mo = mv->get_object(); + + if (mo->instances.empty()) + return; + + if (!m_measuring || mo != m_old_model_object || mv != m_old_model_volume || mo->volumes.size() != m_volumes_matrices.size()) + do_update(mo, mv); // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) - goto UPDATE; + if (!mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) || + !mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + do_update(mo, mv); - for (unsigned int i=0; i < mo->volumes.size(); ++i) - if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || mo->volumes[i]->type() != m_volumes_types[i]) - goto UPDATE; - - return; - -UPDATE: - const indexed_triangle_set& its = mo->volumes.front()->mesh().its; - m_measuring.reset(new Measure::Measuring(its)); - m_plane_models.clear(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - for (const std::vector& triangle_indices : planes_triangles) { - m_plane_models.emplace_back(std::unique_ptr(new GLModel())); - GUI::GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); - int i = 0; - for (int idx : triangle_indices) { - init_data.add_vertex(its.vertices[its.indices[idx][0]]); - init_data.add_vertex(its.vertices[its.indices[idx][1]]); - init_data.add_vertex(its.vertices[its.indices[idx][2]]); - init_data.add_triangle(i, i+1, i+2); - i+=3; + for (unsigned int i = 0; i < mo->volumes.size(); ++i) { + if (!mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) || + mo->volumes[i]->type() != m_volumes_types[i]) { + do_update(mo, mv); + break; } - m_plane_models.back()->init_from(std::move(init_data)); } - - // Let's save what we calculated it from: - m_volumes_matrices.clear(); - m_volumes_types.clear(); - for (const ModelVolume* vol : mo->volumes) { - m_volumes_matrices.push_back(vol->get_matrix()); - m_volumes_types.push_back(vol->type()); - } - m_first_instance_scale = mo->instances.front()->get_scaling_factor(); - m_first_instance_mirror = mo->instances.front()->get_mirror(); - m_old_model_object = mo; } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index e1460c2146..cfdc760608 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -14,6 +14,8 @@ namespace Slic3r { +class ModelVolume; + enum class ModelVolumeType : int; namespace Measure { class Measuring; } @@ -35,12 +37,13 @@ private: // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; - Vec3d m_first_instance_scale; - Vec3d m_first_instance_mirror; + Vec3d m_first_instance_scale{ Vec3d::Ones() }; + Vec3d m_first_instance_mirror{ Vec3d::Ones() }; bool m_mouse_left_down = false; // for detection left_up of this gizmo bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; + const ModelVolume* m_old_model_volume = nullptr; std::vector instances_matrices; int m_mouse_pos_x; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index ce3ba1d4ab..1eef6aced5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -119,21 +119,25 @@ void SelectionInfo::on_update() const Selection& selection = get_pool()->get_canvas()->get_selection(); if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; + m_model_volume = nullptr; m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } - else + else { m_model_object = nullptr; + if (selection.is_single_volume()) + m_model_volume = selection.get_model()->objects[selection.get_object_idx()]->volumes[selection.get_first_volume()->volume_idx()]; + } } void SelectionInfo::on_release() { m_model_object = nullptr; + m_model_volume = nullptr; } int SelectionInfo::get_active_instance() const { - const Selection& selection = get_pool()->get_canvas()->get_selection(); - return selection.get_instance_idx(); + return get_pool()->get_canvas()->get_selection().get_instance_idx(); } @@ -329,12 +333,18 @@ void Raycaster::on_update() { wxBusyCursor wait; const ModelObject* mo = get_pool()->selection_info()->model_object(); + const ModelVolume* mv = get_pool()->selection_info()->model_volume(); - if (! mo) + if (mo == nullptr && mv == nullptr) return; + std::vector mvs; + if (mv != nullptr) + mvs.push_back(const_cast(mv)); + else + mvs = mo->volumes; + std::vector meshes; - const std::vector& mvs = mo->volumes; if (mvs.size() == 1) { assert(mvs.front()->is_model_part()); const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); @@ -342,9 +352,9 @@ void Raycaster::on_update() meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); } if (meshes.empty()) { - for (const ModelVolume* mv : mvs) { - if (mv->is_model_part()) - meshes.push_back(&mv->mesh()); + for (const ModelVolume* v : mvs) { + if (v->is_model_part()) + meshes.push_back(&v->mesh()); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 7dd2c110ea..c8b29f761e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -10,7 +10,7 @@ namespace Slic3r { class ModelObject; - +class ModelVolume; namespace GUI { @@ -153,7 +153,10 @@ public: explicit SelectionInfo(CommonGizmosDataPool* cgdp) : CommonGizmosDataBase(cgdp) {} + // Returns a non-null pointer if the selection is a single full instance ModelObject* model_object() const { return m_model_object; } + // Returns a non-null pointer if the selection is a single volume + ModelVolume* model_volume() const { return m_model_volume; } int get_active_instance() const; float get_sla_shift() const { return m_z_shift; } @@ -163,6 +166,7 @@ protected: private: ModelObject* m_model_object = nullptr; + ModelVolume* m_model_volume = nullptr; // int m_active_inst = -1; float m_z_shift = 0.f; }; From cf144da4fe8dc0797096b4c352db2bde7cc0c96f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 18 Aug 2022 13:57:25 +0200 Subject: [PATCH 085/327] 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 5caff6d3a7..4251110c41 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 9fa38ecd7d..21b0523370 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 42cd216991..452f620c47 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 c260959a13..7b6c729f8d 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 27764a6989..96df8892c9 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 a7d1c9b5e99132e51d84fcd5155ede7da1ecadd0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Aug 2022 13:17:09 +0200 Subject: [PATCH 086/327] Measuring: reworked rendering of hovered features --- src/slic3r/GUI/GLModel.cpp | 104 +++++++++++++++ src/slic3r/GUI/GLModel.hpp | 7 ++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 154 ++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 + 4 files changed, 211 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 5ae7f9fbf5..e05ae7619b 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2274,6 +2274,110 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) return data; } + +GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) +{ + resolution = std::max(4, resolution); + + const unsigned int sectorCount = resolution; + const float sectorStep = 2.0f * float(M_PI) / float(sectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(sectorCount * 4 + 2); + data.reserve_indices(sectorCount * 4 * 3); + + auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { + std::vector ret; + ret.reserve(sectorCount); + for (unsigned int i = 0; i < sectorCount; ++i) { + // from 0 to 2pi + const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; + const float x1 = radius * std::cos(sectorAngle); + const float x2 = radius * std::sin(sectorAngle); + + Vec3f v; + switch (axis) + { + case X: { v = Vec3f(0.0f, x1, x2); break; } + case Y: { v = Vec3f(-x1, 0.0f, x2); break; } + case Z: { v = Vec3f(x1, x2, 0.0f); break; } + default: { assert(false); break; } + } + + ret.emplace_back(v); + } + return ret; + }; + + const std::vector base_vertices = generate_vertices_on_circle(axis, radius); + + Vec3f h; + switch (axis) + { + case X: { h = height * Vec3f::UnitX(); break; } + case Y: { h = height * Vec3f::UnitY(); break; } + case Z: { h = height * Vec3f::UnitZ(); break; } + default: { assert(false); break; } + } + + // stem vertices + for (unsigned int i = 0; i < sectorCount; ++i) { + const Vec3f& v = base_vertices[i]; + const Vec3f n = v.normalized(); + data.add_vertex(v, n); + data.add_vertex(v + h, n); + } + + // stem triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + unsigned int v1 = i * 2; + unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0; + unsigned int v3 = v2 + 1; + unsigned int v4 = v1 + 1; + data.add_triangle(v1, v2, v3); + data.add_triangle(v1, v3, v4); + } + + // bottom cap vertices + Vec3f cap_center = Vec3f::Zero(); + unsigned int cap_center_id = data.vertices_count(); + Vec3f normal; + switch (axis) + { + case X: { normal = -Vec3f::UnitX(); break; } + case Y: { normal = -Vec3f::UnitY(); break; } + case Z: { normal = -Vec3f::UnitZ(); break; } + default: { assert(false); break; } + } + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i], normal); + } + + // bottom cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1); + } + + // top cap vertices + cap_center += h; + cap_center_id = data.vertices_count(); + normal = -normal; + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i] + h, normal); + } + + // top cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1); + } + + return data; +} #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index f3c62d786c..7d2f785573 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -365,7 +365,14 @@ namespace GUI { #if ENABLE_LEGACY_OPENGL_REMOVAL // create a sphere with the given resolution and smooth normals // the origin of the sphere is in its center + // the radius of the sphere is the given value GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); + // create a cylinder with the given resolution and smooth normals + // the axis of the cylinder is the given value + // the radius of the cylinder is the given value + // the height of the cylinder is the given value + // the origin of the cylinder is in the center of the cap face having axis == 0 + GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 92fb8691ef..8b39582822 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -8,6 +8,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/Measure.hpp" +#include "libslic3r/PresetBundle.hpp" #include @@ -21,8 +22,8 @@ static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_sphere.init_from(its_make_sphere(1., M_PI/32.)); - m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); + m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); + m_vbo_cylinder.init_from(smooth_cylinder(Z, 16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -106,7 +107,9 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - return selection.is_single_volume() || selection.is_single_volume_instance(); + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + selection.is_single_full_instance() : + selection.is_single_volume() || selection.is_single_volume_instance(); } void GLGizmoMeasure::on_render() @@ -117,30 +120,27 @@ void GLGizmoMeasure::on_render() const Selection& selection = m_parent.get_selection(); - GLShaderProgram* shader = wxGetApp().get_shader("flat"); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); + shader->set_uniform("emission_factor", 0.25f); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); -#if ENABLE_GL_CORE_PROFILE - if (!OpenGLManager::get_gl_info().is_core_profile()) -#endif // ENABLE_GL_CORE_PROFILE - glsafe(::glLineWidth(2.f)); - if (selection.is_single_volume() || selection.is_single_volume_instance()) { + if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && selection.is_single_full_instance()) || + (selection.is_single_volume() || selection.is_single_volume_instance())) { const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * model_matrix; + const Transform3d& view_matrix = camera.get_view_matrix(); + const float inv_zoom = camera.get_inv_zoom(); - shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - + update_if_needed(); Vec3f pos; @@ -148,7 +148,6 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#define ENABLE_DEBUG_DIALOG 0 #if ENABLE_DEBUG_DIALOG m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); @@ -162,34 +161,46 @@ void GLGizmoMeasure::on_render() #endif // ENABLE_DEBUG_DIALOG std::vector features; - if (m_show_all) { +#if ENABLE_DEBUG_DIALOG + if (m_show_all) { features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), [](const Measure::SurfaceFeature& f) { return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); - } else { + } + else { +#endif // ENABLE_DEBUG_DIALOG std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat) + if (feat.has_value()) features.emplace_back(*feat); - } - - +#if ENABLE_DEBUG_DIALOG + } +#endif // ENABLE_DEBUG_DIALOG for (const Measure::SurfaceFeature& feature : features) { - - if (feature.get_type() == Measure::SurfaceFeatureType::Point) { - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { - const auto& [c, radius, n] = feature.get_circle(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + // render center + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); @@ -198,45 +209,69 @@ void GLGizmoMeasure::on_render() if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); rad *= radius * rad.norm(); - const int N = 20; - for (int i=0; iset_uniform("view_model_matrix", view_feature_matrix); + const int N = 32; + m_vbo_sphere.set_color(HOVER_COLOR); + for (int i = 0; i < N; ++i) { + rad = Eigen::AngleAxisd(2.0 * M_PI / N, n) * rad; + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center + rad) * Geometry::scale_transform(N / 100.0 * inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.render(); } + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + case Measure::SurfaceFeatureType::Edge: + { const auto& [start, end] = feature.get_edge(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); - shader->set_uniform("view_model_matrix", view_feature_matrix); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_cylinder.set_color(HOVER_COLOR); m_vbo_cylinder.render(); - if (feature.get_extra_point()) { - Vec3d pin = *feature.get_extra_point(); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + +/* + std::optional extra_point = feature.get_extra_point(); + if (extra_point.has_value()) { + const Vec3d pin = *extra_point; + const Transform3d feature_matrix = Geometry::translation_transform(pin + selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * + Geometry::scale_transform(inv_zoom) * model_matrix; + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } +*/ + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + case Measure::SurfaceFeatureType::Plane: + { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models.size()); + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_plane_models[idx]->set_color(HOVER_COLOR); m_plane_models[idx]->render(); - } + break; + } + } } - shader->set_uniform("view_model_matrix", view_model_matrix); +#if ENABLE_DEBUG_DIALOG if (m_show_planes) - for (const auto& glmodel : m_plane_models) + for (const auto& glmodel : m_plane_models) { + glmodel->set_color(HOVER_COLOR); glmodel->render(); + } +#endif // ENABLE_DEBUG_DIALOG } glsafe(::glEnable(GL_CULL_FACE)); @@ -263,14 +298,19 @@ void GLGizmoMeasure::update_if_needed() const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); for (const std::vector& triangle_indices : planes_triangles) { m_plane_models.emplace_back(std::unique_ptr(new GLModel())); - GUI::GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); int i = 0; for (int idx : triangle_indices) { - init_data.add_vertex(its.vertices[its.indices[idx][0]]); - init_data.add_vertex(its.vertices[its.indices[idx][1]]); - init_data.add_vertex(its.vertices[its.indices[idx][2]]); + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); init_data.add_triangle(i, i + 1, i + 2); i += 3; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index cfdc760608..72454f475d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -11,6 +11,7 @@ #include +#define ENABLE_DEBUG_DIALOG 0 namespace Slic3r { @@ -48,8 +49,10 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; +#if ENABLE_DEBUG_DIALOG bool m_show_all = false; bool m_show_planes = false; +#endif // ENABLE_DEBUG_DIALOG std::vector> m_plane_models; void update_if_needed(); From 8f06086679eb695d3e79f36044255e0fa6ac4d21 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Aug 2022 13:53:55 +0200 Subject: [PATCH 087/327] Follow-up of a7d1c9b5e99132e51d84fcd5155ede7da1ecadd0 - Simplified code to generate a smooth cylinder --- src/slic3r/GUI/GLModel.cpp | 39 ++++-------------------- src/slic3r/GUI/GLModel.hpp | 13 +++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index e05ae7619b..249a5cce7e 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2275,7 +2275,7 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) return data; } -GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) +GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height) { resolution = std::max(4, resolution); @@ -2287,39 +2287,19 @@ GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radi data.reserve_vertices(sectorCount * 4 + 2); data.reserve_indices(sectorCount * 4 * 3); - auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { + auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) { std::vector ret; ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; - const float x1 = radius * std::cos(sectorAngle); - const float x2 = radius * std::sin(sectorAngle); - - Vec3f v; - switch (axis) - { - case X: { v = Vec3f(0.0f, x1, x2); break; } - case Y: { v = Vec3f(-x1, 0.0f, x2); break; } - case Z: { v = Vec3f(x1, x2, 0.0f); break; } - default: { assert(false); break; } - } - - ret.emplace_back(v); + ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); } return ret; }; - const std::vector base_vertices = generate_vertices_on_circle(axis, radius); - - Vec3f h; - switch (axis) - { - case X: { h = height * Vec3f::UnitX(); break; } - case Y: { h = height * Vec3f::UnitY(); break; } - case Z: { h = height * Vec3f::UnitZ(); break; } - default: { assert(false); break; } - } + const std::vector base_vertices = generate_vertices_on_circle(radius); + const Vec3f h = height * Vec3f::UnitZ(); // stem vertices for (unsigned int i = 0; i < sectorCount; ++i) { @@ -2342,14 +2322,7 @@ GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radi // bottom cap vertices Vec3f cap_center = Vec3f::Zero(); unsigned int cap_center_id = data.vertices_count(); - Vec3f normal; - switch (axis) - { - case X: { normal = -Vec3f::UnitX(); break; } - case Y: { normal = -Vec3f::UnitY(); break; } - case Z: { normal = -Vec3f::UnitZ(); break; } - default: { assert(false); break; } - } + Vec3f normal = -Vec3f::UnitZ(); data.add_vertex(cap_center, normal); for (unsigned int i = 0; i < sectorCount; ++i) { diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 7d2f785573..c5b9f5e095 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -363,16 +363,13 @@ namespace GUI { GLModel::Geometry diamond(unsigned int resolution); #if ENABLE_LEGACY_OPENGL_REMOVAL - // create a sphere with the given resolution and smooth normals + // create a sphere with smooth normals // the origin of the sphere is in its center - // the radius of the sphere is the given value GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); - // create a cylinder with the given resolution and smooth normals - // the axis of the cylinder is the given value - // the radius of the cylinder is the given value - // the height of the cylinder is the given value - // the origin of the cylinder is in the center of the cap face having axis == 0 - GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height); + // create a cylinder with smooth normals + // the axis of the cylinder is the Z axis + // the origin of the cylinder is the center of its bottom cap face + GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8b39582822..32f110d9fd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -23,7 +23,7 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen : GLGizmoBase(parent, icon_filename, sprite_id) { m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); - m_vbo_cylinder.init_from(smooth_cylinder(Z, 16, 5.0f, 1.0f)); + m_vbo_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) From 26650a26588c2039a1743ef7e982de9fd6d58c0e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 11:25:15 +0200 Subject: [PATCH 088/327] Measuring: circle feature rendered using a torus --- src/slic3r/GUI/GLModel.cpp | 47 ++++++++++++++++- src/slic3r/GUI/GLModel.hpp | 4 ++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 67 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 +- 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 249a5cce7e..b36db83ad1 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2292,7 +2292,7 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi - const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; + const float sectorAngle = sectorStep * i; ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); } return ret; @@ -2351,6 +2351,51 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h return data; } + +GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness) +{ + primary_resolution = std::max(4, primary_resolution); + secondary_resolution = std::max(4, secondary_resolution); + const unsigned int torusSectorCount = primary_resolution; + const float torusSectorStep = 2.0f * float(M_PI) / float(torusSectorCount); + const unsigned int sectionSectorCount = secondary_resolution; + const float sectionSectorStep = 2.0f * float(M_PI) / float(sectionSectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torusSectorCount * sectionSectorCount); + data.reserve_indices(torusSectorCount * sectionSectorCount * 2 * 3); + + // vertices + for (unsigned int i = 0; i < torusSectorCount; ++i) { + const float sectionAngle = torusSectorStep * i; + const Vec3f sectionCenter(radius * std::cos(sectionAngle), radius * std::sin(sectionAngle), 0.0f); + for (unsigned int j = 0; j < sectionSectorCount; ++j) { + const float circleAngle = sectionSectorStep * j; + const float thickness_xy = thickness * std::cos(circleAngle); + const float thickness_z = thickness * std::sin(circleAngle); + const Vec3f v(thickness_xy * std::cos(sectionAngle), thickness_xy * std::sin(sectionAngle), thickness_z); + data.add_vertex(sectionCenter + v, (Vec3f)v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torusSectorCount; ++i) { + const unsigned int ii = i * sectionSectorCount; + const unsigned int ii_next = ((i + 1) % torusSectorCount) * sectionSectorCount; + for (unsigned int j = 0; j < sectionSectorCount; ++j) { + const unsigned int j_next = (j + 1) % sectionSectorCount; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } + } + + return data; +} #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index c5b9f5e095..8843d39eeb 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -370,6 +370,10 @@ namespace GUI { // the axis of the cylinder is the Z axis // the origin of the cylinder is the center of its bottom cap face GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); + // create a torus with smooth normals + // the axis of the torus is the Z axis + // the origin of the torus is in its center + GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 32f110d9fd..6e1ba9991e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -22,8 +22,8 @@ static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); - m_vbo_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); + m_sphere.init_from(smooth_sphere(16, 7.5f)); + m_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -188,38 +188,32 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); + m_sphere.set_color(HOVER_COLOR); + m_sphere.render(); break; } case Measure::SurfaceFeatureType::Circle: { const auto& [center, radius, n] = feature.get_circle(); // render center - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d center_view_model_matrix = view_matrix * center_matrix; + shader->set_uniform("view_model_matrix", center_view_model_matrix); + const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", center_view_normal_matrix); + m_sphere.set_color(HOVER_COLOR); + m_sphere.render(); - // Now draw the circle itself - let's take a funny shortcut: - Vec3d rad = n.cross(Vec3d::UnitX()); - if (rad.squaredNorm() < 0.1) - rad = n.cross(Vec3d::UnitY()); - rad *= radius * rad.norm(); - const int N = 32; - m_vbo_sphere.set_color(HOVER_COLOR); - for (int i = 0; i < N; ++i) { - rad = Eigen::AngleAxisd(2.0 * M_PI / N, n) * rad; - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center + rad) * Geometry::scale_transform(N / 100.0 * inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.render(); - } + m_circle.reset(); + m_circle.init_from(smooth_torus(64, 16, float(radius), 5.0f * inv_zoom)); + + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; + shader->set_uniform("view_model_matrix", circle_view_model_matrix); + const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); + m_circle.set_color(HOVER_COLOR); + m_circle.render(); break; } case Measure::SurfaceFeatureType::Edge: @@ -232,23 +226,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_cylinder.set_color(HOVER_COLOR); - m_vbo_cylinder.render(); - -/* - std::optional extra_point = feature.get_extra_point(); - if (extra_point.has_value()) { - const Vec3d pin = *extra_point; - const Transform3d feature_matrix = Geometry::translation_transform(pin + selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * - Geometry::scale_transform(inv_zoom) * model_matrix; - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); - } -*/ + m_cylinder.set_color(HOVER_COLOR); + m_cylinder.render(); break; } case Measure::SurfaceFeatureType::Plane: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 72454f475d..79bc934cbb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -32,8 +32,9 @@ class GLGizmoMeasure : public GLGizmoBase private: std::unique_ptr m_measuring; - GLModel m_vbo_sphere; - GLModel m_vbo_cylinder; + GLModel m_sphere; + GLModel m_cylinder; + GLModel m_circle; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 2abb52ed4c88f9b5b3d52297d356955c1f4039e2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 12:00:04 +0200 Subject: [PATCH 089/327] Measuring: refactoring related to plane models cache --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 41 +++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6e1ba9991e..d6bd5e67be 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -233,23 +233,28 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Plane: { const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models.size()); + assert(idx < m_plane_models_cache.size()); const Transform3d view_model_matrix = view_matrix * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models[idx]->set_color(HOVER_COLOR); - m_plane_models[idx]->render(); + m_plane_models_cache[idx].set_color(HOVER_COLOR); + m_plane_models_cache[idx].render(); break; } } } #if ENABLE_DEBUG_DIALOG - if (m_show_planes) - for (const auto& glmodel : m_plane_models) { - glmodel->set_color(HOVER_COLOR); - glmodel->render(); + if (m_show_planes) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + for (GLModel& glmodel : m_plane_models_cache) { + glmodel.set_color(HOVER_COLOR); + glmodel.render(); } + } #endif // ENABLE_DEBUG_DIALOG } @@ -270,17 +275,14 @@ void GLGizmoMeasure::on_render() void GLGizmoMeasure::update_if_needed() { - auto do_update = [this](const ModelObject* object, const ModelVolume* volume) { - const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; - m_measuring.reset(new Measure::Measuring(its)); - m_plane_models.clear(); + auto update_plane_models_cache = [this](const indexed_triangle_set& its) { + m_plane_models_cache.clear(); const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); for (const std::vector& triangle_indices : planes_triangles) { - m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + m_plane_models_cache.emplace_back(GLModel()); GLModel::Geometry init_data; init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); - int i = 0; + unsigned int i = 0; for (int idx : triangle_indices) { const Vec3f& v0 = its.vertices[its.indices[idx][0]]; const Vec3f& v1 = its.vertices[its.indices[idx][1]]; @@ -293,8 +295,15 @@ void GLGizmoMeasure::update_if_needed() init_data.add_triangle(i, i + 1, i + 2); i += 3; } - m_plane_models.back()->init_from(std::move(init_data)); + m_plane_models_cache.back().init_from(std::move(init_data)); } + }; + + auto do_update = [this, update_plane_models_cache](const ModelObject* object, const ModelVolume* volume) { + const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + + update_plane_models_cache(its); // Let's save what we calculated it from: m_volumes_matrices.clear(); @@ -306,7 +315,7 @@ void GLGizmoMeasure::update_if_needed() m_volumes_matrices.push_back(vol->get_matrix()); m_volumes_types.push_back(vol->type()); } - m_first_instance_scale = object->instances.front()->get_scaling_factor(); + m_first_instance_scale = object->instances.front()->get_scaling_factor(); m_first_instance_mirror = object->instances.front()->get_mirror(); } m_old_model_object = object; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 79bc934cbb..8197ea6863 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -35,6 +35,7 @@ private: GLModel m_sphere; GLModel m_cylinder; GLModel m_circle; + std::vector m_plane_models_cache; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -54,7 +55,6 @@ private: bool m_show_all = false; bool m_show_planes = false; #endif // ENABLE_DEBUG_DIALOG - std::vector> m_plane_models; void update_if_needed(); From d0bf05d1e9760e3d452f612c70bee282b6486c5e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 14:15:20 +0200 Subject: [PATCH 090/327] Refactoring into GLGizmoMeasure::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 67 +++++++++++++----------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d6bd5e67be..844ed8876f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -114,34 +114,20 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { +#if !ENABLE_DEBUG_DIALOG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; +#endif // !ENABLE_DEBUG_DIALOG const Selection& selection = m_parent.get_selection(); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - shader->start_using(); - shader->set_uniform("emission_factor", 0.25f); - - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glEnable(GL_BLEND)); - if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && selection.is_single_full_instance()) || (selection.is_single_volume() || selection.is_single_volume_instance())) { + update_if_needed(); + const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - const float inv_zoom = camera.get_inv_zoom(); - - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - update_if_needed(); Vec3f pos; Vec3f normal; @@ -165,19 +151,42 @@ void GLGizmoMeasure::on_render() if (m_show_all) { features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); } else { + if (!m_parent.is_mouse_dragging()) { #endif // ENABLE_DEBUG_DIALOG - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat.has_value()) + features.emplace_back(*feat); #if ENABLE_DEBUG_DIALOG + } } #endif // ENABLE_DEBUG_DIALOG - + +#if ENABLE_DEBUG_DIALOG + if (features.empty() && !m_show_planes) +#else + if (features.empty()) +#endif // ENABLE_DEBUG_DIALOG + return; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("emission_factor", 0.25f); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + const Transform3d& view_matrix = camera.get_view_matrix(); + const float inv_zoom = camera.get_inv_zoom(); + for (const Measure::SurfaceFeature& feature : features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: @@ -256,18 +265,12 @@ void GLGizmoMeasure::on_render() } } #endif // ENABLE_DEBUG_DIALOG + shader->stop_using(); } - - glsafe(::glEnable(GL_CULL_FACE)); - glsafe(::glDisable(GL_BLEND)); - - shader->stop_using(); } - - #if ! ENABLE_LEGACY_OPENGL_REMOVAL #error NOT IMPLEMENTED #endif From 38c52941d68723b887b935622c6dd1f50fc46da6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 15:02:46 +0200 Subject: [PATCH 091/327] Added tech ENABLE_MEASURE_GIZMO_DEBUG to embed debug code related to GLGizmoMeasure --- src/libslic3r/Measure.cpp | 3 ++ src/libslic3r/Measure.hpp | 4 +- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 50 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 6 +-- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 8c7254f85b..9cb46edfc1 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1,3 +1,4 @@ +#include "libslic3r/libslic3r.h" #include "Measure.hpp" #include "libslic3r/Geometry/Circle.hpp" @@ -407,10 +408,12 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} +#if ENABLE_MEASURE_GIZMO_DEBUG std::vector Measuring::get_all_features() const { return priv->get_all_features(); } +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index fe0b1687a1..98517b991b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -69,9 +69,11 @@ public: explicit Measuring(const indexed_triangle_set& its); ~Measuring(); +#if ENABLE_MEASURE_GIZMO_DEBUG // Return a reference to a list of all features identified on the its. // Use only for debugging. Expensive, do not call often. - [[deprecated]] std::vector get_all_features() const; + std::vector get_all_features() const; +#endif // ENABLE_MEASURE_GIZMO_DEBUG // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ee2c0cd804..58720801f8 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,6 +78,8 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) +// Enable debug code for Measure Gizmo +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_2_5_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 844ed8876f..7ca051c7d1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -114,11 +114,11 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { -#if !ENABLE_DEBUG_DIALOG +#if !ENABLE_MEASURE_GIZMO_DEBUG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; -#endif // !ENABLE_DEBUG_DIALOG +#endif // !ENABLE_MEASURE_GIZMO_DEBUG const Selection& selection = m_parent.get_selection(); @@ -134,7 +134,7 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); m_imgui->checkbox(wxString("Show all planes"), m_show_planes); @@ -144,33 +144,35 @@ void GLGizmoMeasure::on_render() m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); m_imgui->end(); -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::vector features; -#if ENABLE_DEBUG_DIALOG - if (m_show_all) { - features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); +#if ENABLE_MEASURE_GIZMO_DEBUG + if (m_show_all || m_show_planes) { + features = m_measuring->get_all_features(); + if (!m_show_planes) + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); + if (!m_show_all) + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature& f) { + return f.get_type() != Measure::SurfaceFeatureType::Plane; + }), features.end()); } else { if (!m_parent.is_mouse_dragging()) { -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); if (feat.has_value()) features.emplace_back(*feat); -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG } } -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG -#if ENABLE_DEBUG_DIALOG - if (features.empty() && !m_show_planes) -#else if (features.empty()) -#endif // ENABLE_DEBUG_DIALOG return; GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -253,18 +255,6 @@ void GLGizmoMeasure::on_render() } } } -#if ENABLE_DEBUG_DIALOG - if (m_show_planes) { - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - for (GLModel& glmodel : m_plane_models_cache) { - glmodel.set_color(HOVER_COLOR); - glmodel.render(); - } - } -#endif // ENABLE_DEBUG_DIALOG shader->stop_using(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 8197ea6863..49ab7fce8a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -11,8 +11,6 @@ #include -#define ENABLE_DEBUG_DIALOG 0 - namespace Slic3r { class ModelVolume; @@ -51,10 +49,10 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG bool m_show_all = false; bool m_show_planes = false; -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG void update_if_needed(); From 1787f780b60c02344f914087d280a6dd98877080 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 25 Aug 2022 13:01:26 +0200 Subject: [PATCH 092/327] Measuring: code for Measure gizmo embedded into new tech ENABLE_MEASURE_GIZMO --- src/libslic3r/Technologies.hpp | 4 +++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 ++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 11 ++++------- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 ++++ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 58720801f8..5eadc8d098 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,8 +78,10 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) +// Enable Measure Gizmo +#define ENABLE_MEASURE_GIZMO (1 && ENABLE_LEGACY_OPENGL_REMOVAL) // Enable debug code for Measure Gizmo -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_2_5_0_ALPHA1) +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 7ca051c7d1..73ee87840a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -14,6 +14,8 @@ #include +#if ENABLE_MEASURE_GIZMO + namespace Slic3r { namespace GUI { @@ -261,11 +263,6 @@ void GLGizmoMeasure::on_render() -#if ! ENABLE_LEGACY_OPENGL_REMOVAL - #error NOT IMPLEMENTED -#endif - - void GLGizmoMeasure::update_if_needed() { auto update_plane_models_cache = [this](const indexed_triangle_set& its) { @@ -345,3 +342,5 @@ void GLGizmoMeasure::update_if_needed() } // namespace GUI } // namespace Slic3r + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 49ab7fce8a..742c791131 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -1,15 +1,10 @@ #ifndef slic3r_GLGizmoMeasure_hpp_ #define slic3r_GLGizmoMeasure_hpp_ +#if ENABLE_MEASURE_GIZMO + #include "GLGizmoBase.hpp" -#if ENABLE_LEGACY_OPENGL_REMOVAL #include "slic3r/GUI/GLModel.hpp" -#else -#include "slic3r/GUI/3DScene.hpp" -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - -#include namespace Slic3r { @@ -80,4 +75,6 @@ protected: } // namespace GUI } // namespace Slic3r +#endif // ENABLE_MEASURE_GIZMO + #endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 6d25f84fbc..ff24dd84a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,7 +21,9 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#if ENABLE_MEASURE_GIZMO #include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" +#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -107,7 +109,9 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); +#if ENABLE_MEASURE_GIZMO m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); +#endif // ENABLE_MEASURE_GIZMO m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); From 8833bc3138676461ed443f19f900e80e0402bac3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 29 Aug 2022 12:55:34 +0200 Subject: [PATCH 093/327] Measuring: Measure gizmo features registered for raycasted picking --- src/libslic3r/Measure.hpp | 10 ++ src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 4 +- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/GLModel.cpp | 15 +++ src/slic3r/GUI/GLModel.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 132 ++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 16 ++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 +- 10 files changed, 160 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 98517b991b..2f27a72598 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -49,6 +49,16 @@ public: // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } + bool operator == (const SurfaceFeature& other) const { + if (this->m_type != other.m_type) return false; + if (!this->m_pt1.isApprox(other.m_pt1)) return false; + if (!this->m_pt2.isApprox(other.m_pt2)) return false; + if (this->m_pt3.has_value() && !other.m_pt3.has_value()) return false; + if (!this->m_pt3.has_value() && other.m_pt3.has_value()) return false; + if (this->m_pt3.has_value() && other.m_pt3.has_value() && !(*this->m_pt3).isApprox(*other.m_pt3)) return false; + return this->m_value == other.m_value; + } + private: SurfaceFeatureType m_type = SurfaceFeatureType::Undef; Vec3d m_pt1; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5eadc8d098..4e2011b3b4 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -79,7 +79,7 @@ #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo -#define ENABLE_MEASURE_GIZMO (1 && ENABLE_LEGACY_OPENGL_REMOVAL) +#define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) // Enable debug code for Measure Gizmo #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dcc0d2db4f..9c98a20e7a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2234,7 +2234,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_LEGACY_OPENGL_REMOVAL volume.model.init_from(mesh); #if ENABLE_RAYCAST_PICKING - volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); #endif // ENABLE_RAYCAST_PICKING #else volume.indexed_vertex_array.load_mesh(mesh); @@ -2254,7 +2254,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_RAYCAST_PICKING const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); volume.model.init_from(new_mesh); - volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); + volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); #else volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); #endif // ENABLE_RAYCAST_PICKING diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4b78b66d99..b058386f56 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -680,7 +680,7 @@ public: #if ENABLE_RAYCAST_PICKING std::shared_ptr add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, - const Transform3d& trafo, bool use_back_faces = false) { + const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) { return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces); } void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) { diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index b36db83ad1..445711f885 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -278,6 +278,21 @@ void GLModel::Geometry::remove_vertex(size_t id) } } +indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const +{ + indexed_triangle_set its; + its.vertices.reserve(vertices_count()); + for (size_t i = 0; i < vertices_count(); ++i) { + its.vertices.emplace_back(extract_position_3(i)); + } + its.indices.reserve(indices_count() / 3); + for (size_t i = 0; i < indices_count() / 3; ++i) { + const size_t tri_id = i * 3; + its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2)); + } + return its; +} + size_t GLModel::Geometry::vertex_stride_floats(const Format& format) { switch (format.vertex_layout) diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 8843d39eeb..d4d7307855 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -129,6 +129,8 @@ namespace GUI { size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); } + indexed_triangle_set get_as_indexed_triangle_set() const; + static size_t vertex_stride_floats(const Format& format); static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index bbae3f2429..99769e0061 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -114,7 +114,7 @@ void GLGizmoHollow::on_register_raycasters_for_picking() if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; for (int i = 0; i < (int)drain_holes.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster)); } update_raycasters_for_picking_transform(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 73ee87840a..e132cf2ce1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -7,7 +7,6 @@ #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/Measure.hpp" #include "libslic3r/PresetBundle.hpp" #include @@ -20,12 +19,22 @@ namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const int POINT_ID = 100; +static const int EDGE_ID = 200; +static const int CIRCLE_ID = 300; +static const int CIRCLE_CENTER_ID = 301; +static const int PLANE_ID = 400; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_sphere.init_from(smooth_sphere(16, 7.5f)); - m_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); + GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(sphere_geometry.get_as_indexed_triangle_set()))); + m_sphere.model.init_from(std::move(sphere_geometry)); + + GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(cylinder_geometry.get_as_indexed_triangle_set()))); + m_cylinder.model.init_from(std::move(cylinder_geometry)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -130,6 +139,7 @@ void GLGizmoMeasure::on_render() const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); + const float inv_zoom = (float)camera.get_inv_zoom(); Vec3f pos; Vec3f normal; @@ -174,8 +184,70 @@ void GLGizmoMeasure::on_render() } #endif // ENABLE_MEASURE_GIZMO_DEBUG - if (features.empty()) - return; + if (m_features != features) { + GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_features = features; + if (m_features.empty()) + return; + +#if !ENABLE_MEASURE_GIZMO_DEBUG + for (const Measure::SurfaceFeature& feature : m_features) { + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); + + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } + } +#endif // !ENABLE_MEASURE_GIZMO_DEBUG + } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -189,9 +261,8 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const Transform3d& view_matrix = camera.get_view_matrix(); - const float inv_zoom = camera.get_inv_zoom(); - for (const Measure::SurfaceFeature& feature : features) { + for (const Measure::SurfaceFeature& feature : m_features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: { @@ -201,8 +272,11 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.set_color(HOVER_COLOR); - m_sphere.render(); + m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); break; } case Measure::SurfaceFeatureType::Circle: @@ -214,19 +288,22 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", center_view_model_matrix); const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.set_color(HOVER_COLOR); - m_sphere.render(); - - m_circle.reset(); - m_circle.init_from(smooth_torus(64, 16, float(radius), 5.0f * inv_zoom)); + m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.render(); + auto it = m_raycasters.find(CIRCLE_CENTER_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; shader->set_uniform("view_model_matrix", circle_view_model_matrix); const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.set_color(HOVER_COLOR); - m_circle.render(); + m_circle.model.set_color(HOVER_COLOR); + m_circle.model.render(); + it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); break; } case Measure::SurfaceFeatureType::Edge: @@ -239,8 +316,11 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.set_color(HOVER_COLOR); - m_cylinder.render(); + m_cylinder.model.set_color(HOVER_COLOR); + m_cylinder.model.render(); + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); break; } case Measure::SurfaceFeatureType::Plane: @@ -253,6 +333,9 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); m_plane_models_cache[idx].set_color(HOVER_COLOR); m_plane_models_cache[idx].render(); + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); break; } } @@ -340,6 +423,19 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::on_register_raycasters_for_picking() +{ + // the features 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 GLGizmoMeasure::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.set_raycaster_gizmos_on_top(false); + m_raycasters.clear(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 742c791131..9decf82fdc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "libslic3r/Measure.hpp" namespace Slic3r { @@ -25,10 +26,14 @@ class GLGizmoMeasure : public GLGizmoBase private: std::unique_ptr m_measuring; - GLModel m_sphere; - GLModel m_cylinder; - GLModel m_circle; + PickingModel m_sphere; + PickingModel m_cylinder; + PickingModel m_circle; + PickingModel m_plane; + std::vector m_plane_models_cache; + std::map> m_raycasters; + std::vector m_features; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -53,7 +58,7 @@ private: public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - + /// /// Apply rotation on select plane /// @@ -70,6 +75,9 @@ protected: void on_render() override; void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; + + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 026fae17b6..237b101617 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -148,8 +148,8 @@ void GLGizmoSlaSupports::on_register_raycasters_for_picking() if (m_editing_mode && !m_editing_cache.empty()) { for (size_t i = 0; i < m_editing_cache.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()), - m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster), + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster)); } update_raycasters_for_picking_transform(); } From 4675ae2173ff2a9477f77b5bc321c8ee85e504ac Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 14:30:38 +0200 Subject: [PATCH 094/327] Measuring: Added Measure gizmo imgui dialog + removed tech ENABLE_MEASURE_GIZMO_DEBUG + locking of features by pressing CTRL key --- src/libslic3r/Measure.cpp | 2 - src/libslic3r/Measure.hpp | 6 +- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.hpp | 5 + src/slic3r/GUI/GUI_Utils.hpp | 12 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 365 ++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 19 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 58 ++-- 9 files changed, 311 insertions(+), 162 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 9cb46edfc1..cdde276dda 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -408,12 +408,10 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -#if ENABLE_MEASURE_GIZMO_DEBUG std::vector Measuring::get_all_features() const { return priv->get_all_features(); } -#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 2f27a72598..95d8f34cba 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -59,6 +59,10 @@ public: return this->m_value == other.m_value; } + bool operator != (const SurfaceFeature& other) const { + return !operator == (other); + } + private: SurfaceFeatureType m_type = SurfaceFeatureType::Undef; Vec3d m_pt1; @@ -79,11 +83,9 @@ public: explicit Measuring(const indexed_triangle_set& its); ~Measuring(); -#if ENABLE_MEASURE_GIZMO_DEBUG // Return a reference to a list of all features identified on the its. // Use only for debugging. Expensive, do not call often. std::vector get_all_features() const; -#endif // ENABLE_MEASURE_GIZMO_DEBUG // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4e2011b3b4..a5125fba95 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -80,8 +80,6 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) -// Enable debug code for Measure Gizmo -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b058386f56..effd15d5cb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -19,6 +19,9 @@ #if ENABLE_RAYCAST_PICKING #include "SceneRaycaster.hpp" #endif // ENABLE_RAYCAST_PICKING +#if ENABLE_MEASURE_GIZMO +#include "GUI_Utils.hpp" +#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/Slicing.hpp" @@ -135,6 +138,7 @@ private: wxTimer* m_timer; }; +#if !ENABLE_MEASURE_GIZMO class KeyAutoRepeatFilter { size_t m_count{ 0 }; @@ -144,6 +148,7 @@ public: void reset_count() { m_count = 0; } bool is_first() const { return m_count == 0; } }; +#endif // !ENABLE_MEASURE_GIZMO wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index d7d3529fee..e2152edd6d 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -416,6 +416,18 @@ public: ~TaskTimer(); }; +#if ENABLE_MEASURE_GIZMO +class KeyAutoRepeatFilter +{ + size_t m_count{ 0 }; + +public: + void increase_count() { ++m_count; } + void reset_count() { m_count = 0; } + bool is_first() const { return m_count == 0; } +}; +#endif // ENABLE_MEASURE_GIZMO + }} #endif diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e132cf2ce1..2a856fb830 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -15,10 +15,26 @@ #if ENABLE_MEASURE_GIZMO +std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } + case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Point"); } + case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } + case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } + case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + } +} + namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.2f, 0.8f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; + static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; @@ -42,40 +58,39 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_pos_x = mouse_event.GetX(); m_mouse_pos_y = mouse_event.GetY(); - if (mouse_event.Moving()) { // only for sure m_mouse_left_down = false; return false; } - if (mouse_event.LeftDown()) { + else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; - + m_mouse_left_down = true; return true; } // fix: prevent restart gizmo when reselect object // take responsibility for left up - if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; + if (m_parent.get_first_hover_volume_idx() >= 0) + m_mouse_left_down = true; - } else if (mouse_event.LeftUp()) { + } + else if (mouse_event.LeftUp()) { if (m_mouse_left_down) { // responsible for mouse left up after selecting plane m_mouse_left_down = false; return true; } - } else if (mouse_event.Leaving()) { - m_mouse_left_down = false; } + else if (mouse_event.Leaving()) + m_mouse_left_down = false; + return false; } - - void GLGizmoMeasure::data_changed() { - const Selection & selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); const ModelObject* model_object = nullptr; const ModelVolume* model_volume = nullptr; if (selection.is_single_full_instance() || @@ -87,29 +102,43 @@ void GLGizmoMeasure::data_changed() update_if_needed(); } - - -bool GLGizmoMeasure::on_init() +bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { - // FIXME m_shortcut_key = WXK_CONTROL_F; + if (action == SLAGizmoEventType::CtrlDown) { + if (m_ctrl_kar_filter.is_first()) { + if (!m_features.empty()) + m_mode = EMode::ExtendedSelection; + } + + m_ctrl_kar_filter.increase_count(); + } + else if (action == SLAGizmoEventType::CtrlUp) { + m_ctrl_kar_filter.reset_count(); + m_mode = EMode::BasicSelection; + } + return true; } - +bool GLGizmoMeasure::on_init() +{ + m_shortcut_key = WXK_CONTROL_U; + return true; +} void GLGizmoMeasure::on_set_state() { + if (m_state == Off) + m_ctrl_kar_filter.reset_count(); + else + m_mode = EMode::BasicSelection; } - - CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const { return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::Raycaster)); } - - std::string GLGizmoMeasure::on_get_name() const { return _u8L("Measure"); @@ -125,11 +154,9 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { -#if !ENABLE_MEASURE_GIZMO_DEBUG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; -#endif // !ENABLE_MEASURE_GIZMO_DEBUG const Selection& selection = m_parent.get_selection(); @@ -146,107 +173,76 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#if ENABLE_MEASURE_GIZMO_DEBUG - m_imgui->begin(std::string("DEBUG")); - m_imgui->checkbox(wxString("Show all features"), m_show_all); - m_imgui->checkbox(wxString("Show all planes"), m_show_planes); - ImGui::Separator(); - m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); - m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); - m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); - m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - m_imgui->end(); -#endif // ENABLE_MEASURE_GIZMO_DEBUG - std::vector features; -#if ENABLE_MEASURE_GIZMO_DEBUG - if (m_show_all || m_show_planes) { - features = m_measuring->get_all_features(); - if (!m_show_planes) - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); - if (!m_show_all) - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() != Measure::SurfaceFeatureType::Plane; - }), features.end()); + if (m_mode == EMode::BasicSelection) { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat.has_value()) + features.emplace_back(*feat); } - else { - if (!m_parent.is_mouse_dragging()) { -#endif // ENABLE_MEASURE_GIZMO_DEBUG - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); -#if ENABLE_MEASURE_GIZMO_DEBUG - } - } -#endif // ENABLE_MEASURE_GIZMO_DEBUG - if (m_features != features) { - GLGizmoMeasure::on_unregister_raycasters_for_picking(); - m_features = features; - if (m_features.empty()) - return; + if (m_mode == EMode::BasicSelection) { + if (m_features != features) { + GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_features = features; + if (m_features.empty()) + return; -#if !ENABLE_MEASURE_GIZMO_DEBUG - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); - - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; - - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; + for (const Measure::SurfaceFeature& feature : m_features) { + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } } } -#endif // !ENABLE_MEASURE_GIZMO_DEBUG } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -262,6 +258,13 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); + ColorRGBA color; + switch (m_mode) + { + case EMode::BasicSelection: { color = BASIC_HOVER_COLOR; break; } + case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } + } + for (const Measure::SurfaceFeature& feature : m_features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: @@ -272,7 +275,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.set_color(color); m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -288,7 +291,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", center_view_model_matrix); const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.set_color(color); m_sphere.model.render(); auto it = m_raycasters.find(CIRCLE_CENTER_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -299,7 +302,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", circle_view_model_matrix); const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(HOVER_COLOR); + m_circle.model.set_color(color); m_circle.model.render(); it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -316,7 +319,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(HOVER_COLOR); + m_cylinder.model.set_color(color); m_cylinder.model.render(); auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -331,7 +334,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(HOVER_COLOR); + m_plane_models_cache[idx].set_color(color); m_plane_models_cache[idx].render(); auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -344,8 +347,6 @@ void GLGizmoMeasure::on_render() } } - - void GLGizmoMeasure::update_if_needed() { auto update_plane_models_cache = [this](const indexed_triangle_set& its) { @@ -423,6 +424,124 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) +{ + static Measure::SurfaceFeature last_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + + static float last_y = 0.0f; + static float last_h = 0.0f; + + m_imgui->begin(_L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + // 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 (m_features.empty()) + m_imgui->text(_u8L("Select features to measure")); + + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; + + const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + Measure::SurfaceFeature curr_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + for (size_t i = 0; i < m_features.size(); ++i) { + curr_feature = m_features[i]; + const Measure::SurfaceFeatureType type = curr_feature.get_type(); + if (type != Measure::SurfaceFeatureType::Undef) { + const std::string header = surface_feature_type_as_string(type) + std::string("##") + std::to_string(i); + if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("Data", 2)) { + switch (type) + { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * curr_feature.get_point(); + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); + add_row_to_table(_u8L("Position") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = curr_feature.get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", from.x(), from.y(), from.z()); + add_row_to_table(_u8L("From") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", to.x(), to.y(), to.z()); + add_row_to_table(_u8L("To") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = curr_feature.get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", center.x(), center.y(), center.z()); + add_row_to_table(_u8L("Center") + ":", std::string(buf)); + sprintf(buf, "%.3f", radius); + add_row_to_table(_u8L("Radius") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); + add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = curr_feature.get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", origin.x(), origin.y(), origin.z()); + add_row_to_table(_u8L("Origin") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); + add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + break; + } + } + ImGui::EndTable(); + } + } + } + } + + if (last_feature != curr_feature) { + // the dialog may have changed its size, ask for an extra frame to render it properly + last_feature = curr_feature; + m_imgui->set_requires_extra_frame(); + } + + if (!m_features.empty()) { + static const std::string ctrl = +#ifdef __APPLE__ + "⌘" +#else + "Ctrl" +#endif //__APPLE__ + ; + + ImGui::Separator(); + m_imgui->text(_u8L("Press") + " " + ctrl + " " + _u8L("to enable extended selection")); + } + m_imgui->end(); +} + void GLGizmoMeasure::on_register_raycasters_for_picking() { // the features are rendered on top of the scene, so the raytraced picker should take it into account diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9decf82fdc..13c46a48d9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/GUI_Utils.hpp" #include "libslic3r/Measure.hpp" namespace Slic3r { @@ -18,12 +19,19 @@ namespace Measure { class Measuring; } namespace GUI { +enum class SLAGizmoEventType : unsigned char; class GLGizmoMeasure : public GLGizmoBase { // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. -private: + enum class EMode : unsigned char + { + BasicSelection, + ExtendedSelection + }; + + EMode m_mode{ EMode::BasicSelection }; std::unique_ptr m_measuring; PickingModel m_sphere; @@ -49,10 +57,8 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; -#if ENABLE_MEASURE_GIZMO_DEBUG - bool m_show_all = false; - bool m_show_planes = false; -#endif // ENABLE_MEASURE_GIZMO_DEBUG + + KeyAutoRepeatFilter m_ctrl_kar_filter; void update_if_needed(); @@ -68,6 +74,8 @@ public: void data_changed() override; + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + protected: bool on_init() override; std::string on_get_name() const override; @@ -76,6 +84,7 @@ protected: void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; + virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index c8b29f761e..c3aa1691f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -24,6 +24,10 @@ enum class SLAGizmoEventType : unsigned char { Dragging, Delete, SelectAll, +#if ENABLE_MEASURE_GIZMO + CtrlDown, + CtrlUp, +#endif // ENABLE_MEASURE_GIZMO ShiftUp, AltUp, ApplyChanges, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ff24dd84a3..fbf0afe13a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -294,6 +294,10 @@ 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); +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure) + return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); +#endif // ENABLE_MEASURE_GIZMO else return false; } @@ -499,8 +503,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) bool processed = false; - if ((evt.GetModifiers() & ctrlMask) != 0) - { + if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { #ifdef __APPLE__ @@ -518,15 +521,13 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } } - else if (!evt.HasModifiers()) - { + else if (!evt.HasModifiers()) { switch (keyCode) { // key ESC case WXK_ESCAPE: { - if (m_current != Undefined) - { + if (m_current != Undefined) { if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) reset_all_states(); @@ -563,8 +564,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'A': case 'a': { - if (m_current == SlaSupports) - { + if (m_current == SlaSupports) { gizmo_event(SLAGizmoEventType::AutomaticGeneration); // set as processed no matter what's returned by gizmo_event() to avoid the calling canvas to process 'A' as arrange processed = true; @@ -582,8 +582,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'F': case 'f': { - if (m_current == Scale) - { + if (m_current == Scale) { if (!is_dragging()) wxGetApp().plater()->scale_selection_to_fit_print_volume(); @@ -595,8 +594,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } - if (!processed && !evt.HasModifiers()) - { + if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) processed = true; } @@ -612,10 +610,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) const int keyCode = evt.GetKeyCode(); bool processed = false; - if (evt.GetEventType() == wxEVT_KEY_UP) - { - if (m_current == SlaSupports || m_current == Hollow) - { + if (evt.GetEventType() == wxEVT_KEY_UP) { + if (m_current == SlaSupports || m_current == Hollow) { bool is_editing = true; bool is_rectangle_dragging = false; @@ -629,33 +625,33 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); } - if (keyCode == WXK_SHIFT) - { + if (keyCode == WXK_SHIFT) { // shift has been just released - SLA gizmo might want to close rectangular selection. if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging)) processed = true; } - else if (keyCode == WXK_ALT) - { + else if (keyCode == WXK_ALT) { // alt has been just released - SLA gizmo might want to close rectangular selection. if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging)) processed = true; } } +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure && keyCode == WXK_CONTROL) { + gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false); + } +#endif // ENABLE_MEASURE_GIZMO // if (processed) // m_parent.set_cursor(GLCanvas3D::Standard); } - 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()) - { + 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()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } - else if (m_current == Cut) - { + 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()); @@ -668,11 +664,17 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } default: { break; } } - } else if (m_current == Simplify && keyCode == WXK_ESCAPE) { + } + else if (m_current == Simplify && keyCode == WXK_ESCAPE) { GLGizmoSimplify *simplify = dynamic_cast(get_current()); if (simplify != nullptr) processed = simplify->on_esc_key_down(); } +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure && keyCode == WXK_CONTROL) { + gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true); + } +#endif // ENABLE_MEASURE_GIZMO } if (processed) From db1b2fbfc111f98ae2f9f380e53cead6b7b5b433 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 15:38:29 +0200 Subject: [PATCH 095/327] Refactoring of GLGizmoMeasure to simplify code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 329 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 7 +- 2 files changed, 167 insertions(+), 169 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2a856fb830..2c040c9c3b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -55,8 +55,7 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { - m_mouse_pos_x = mouse_event.GetX(); - m_mouse_pos_y = mouse_event.GetY(); + m_mouse_pos = { double(mouse_event.GetX()), double(mouse_event.GetY()) }; if (mouse_event.Moving()) { // only for sure @@ -106,7 +105,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po { if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (!m_features.empty()) + if (m_curr_feature.has_value()) m_mode = EMode::ExtendedSelection; } @@ -171,80 +170,84 @@ void GLGizmoMeasure::on_render() Vec3f pos; Vec3f normal; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, pos, normal, nullptr, &facet_idx); - std::vector features; - if (m_mode == EMode::BasicSelection) { - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); - } + std::optional curr_feature; + if (m_mode == EMode::BasicSelection) + curr_feature = m_measuring->get_feature(facet_idx, pos.cast()); if (m_mode == EMode::BasicSelection) { - if (m_features != features) { + if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); - m_features = features; - if (m_features.empty()) + m_curr_feature = curr_feature; + if (!m_curr_feature.has_value()) return; - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); + switch (m_curr_feature->get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = m_curr_feature->get_circle(); - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; + // TODO: check for changed inv_zoom - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = m_curr_feature->get_plane(); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + // TODO: check for changed idx + + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } } } } + if (!m_curr_feature.has_value()) + return; + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -265,84 +268,84 @@ void GLGizmoMeasure::on_render() case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } } - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d center_view_model_matrix = view_matrix * center_matrix; - shader->set_uniform("view_model_matrix", center_view_model_matrix); - const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(color); - m_sphere.model.render(); - auto it = m_raycasters.find(CIRCLE_CENTER_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); - - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; - shader->set_uniform("view_model_matrix", circle_view_model_matrix); - const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(color); - m_circle.model.render(); - it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = feature.get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(color); - m_cylinder.model.render(); - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(color); - m_plane_models_cache[idx].render(); - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); - break; - } - } + switch (m_curr_feature->get_type()) { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = m_curr_feature->get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_sphere.model.set_color(color); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + break; } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = m_curr_feature->get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d center_view_model_matrix = view_matrix * center_matrix; + shader->set_uniform("view_model_matrix", center_view_model_matrix); + const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", center_view_normal_matrix); + m_sphere.model.set_color(color); + m_sphere.model.render(); + auto it = m_raycasters.find(CIRCLE_CENTER_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; + shader->set_uniform("view_model_matrix", circle_view_model_matrix); + const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); + m_circle.model.set_color(color); + m_circle.model.render(); + it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = m_curr_feature->get_edge(); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_cylinder.model.set_color(color); + m_cylinder.model.render(); + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = m_curr_feature->get_plane(); + assert(idx < m_plane_models_cache.size()); + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_plane_models_cache[idx].set_color(color); + m_plane_models_cache[idx].render(); + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + break; + } + } + shader->stop_using(); } } @@ -426,7 +429,8 @@ void GLGizmoMeasure::update_if_needed() void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { - static Measure::SurfaceFeature last_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + static std::optional last_feature; + static EMode last_mode = EMode::BasicSelection; static float last_y = 0.0f; static float last_h = 0.0f; @@ -446,31 +450,27 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - if (m_features.empty()) + if (!m_curr_feature.has_value()) m_imgui->text(_u8L("Select features to measure")); + else { + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; - auto add_row_to_table = [this](const wxString& label, const std::string& value) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); - ImGui::TableSetColumnIndex(1); - m_imgui->text(value); - }; - - const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - Measure::SurfaceFeature curr_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); - for (size_t i = 0; i < m_features.size(); ++i) { - curr_feature = m_features[i]; - const Measure::SurfaceFeatureType type = curr_feature.get_type(); + const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); if (type != Measure::SurfaceFeatureType::Undef) { - const std::string header = surface_feature_type_as_string(type) + std::string("##") + std::to_string(i); - if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::CollapsingHeader(surface_feature_type_as_string(type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { if (ImGui::BeginTable("Data", 2)) { switch (type) { case Measure::SurfaceFeatureType::Point: { - const Vec3d position = volume_matrix * curr_feature.get_point(); + const Vec3d position = volume_matrix * m_curr_feature->get_point(); char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); add_row_to_table(_u8L("Position") + ":", std::string(buf)); @@ -478,7 +478,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Edge: { - auto [from, to] = curr_feature.get_edge(); + auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; char buf[1024]; @@ -490,7 +490,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Circle: { - auto [center, radius, normal] = curr_feature.get_circle(); + auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; char buf[1024]; @@ -504,7 +504,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Plane: { - auto [idx, normal, origin] = curr_feature.get_plane(); + auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; char buf[1024]; @@ -521,14 +521,15 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } - if (last_feature != curr_feature) { + if (last_feature != m_curr_feature || last_mode != m_mode) { // the dialog may have changed its size, ask for an extra frame to render it properly - last_feature = curr_feature; + last_feature = m_curr_feature; + last_mode = m_mode; m_imgui->set_requires_extra_frame(); } - if (!m_features.empty()) { - static const std::string ctrl = + if (m_curr_feature.has_value() && m_mode == EMode::BasicSelection) { + static const std::string ctrl = #ifdef __APPLE__ "⌘" #else diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 13c46a48d9..093584c7f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -41,7 +41,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; - std::vector m_features; + std::optional m_curr_feature; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -50,13 +50,10 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_mirror{ Vec3d::Ones() }; bool m_mouse_left_down = false; // for detection left_up of this gizmo - bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; const ModelVolume* m_old_model_volume = nullptr; - std::vector instances_matrices; - int m_mouse_pos_x; - int m_mouse_pos_y; + Vec2d m_mouse_pos{ Vec2d::Zero() }; KeyAutoRepeatFilter m_ctrl_kar_filter; From 6694817cd6a893c5ce34f6628a36cdeb5c71119b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 15:50:11 +0200 Subject: [PATCH 096/327] Refactoring of GLGizmoMeasure::on_render_input_window to simplify code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 38 +++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 6 ++-- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2c040c9c3b..3e803fa603 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -460,6 +460,16 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text(value); }; + auto format_double = [](double value) { + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); + }; + auto format_vec3 = [](const Vec3d& v) { + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); + }; const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); @@ -471,9 +481,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit case Measure::SurfaceFeatureType::Point: { const Vec3d position = volume_matrix * m_curr_feature->get_point(); - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); - add_row_to_table(_u8L("Position") + ":", std::string(buf)); + add_row_to_table(_u8L("Position") + ":", format_vec3(position)); break; } case Measure::SurfaceFeatureType::Edge: @@ -481,11 +489,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", from.x(), from.y(), from.z()); - add_row_to_table(_u8L("From") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", to.x(), to.y(), to.z()); - add_row_to_table(_u8L("To") + ":", std::string(buf)); + add_row_to_table(_u8L("From") + ":", format_vec3(from)); + add_row_to_table(_u8L("To") + ":", format_vec3(to)); break; } case Measure::SurfaceFeatureType::Circle: @@ -493,13 +498,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", center.x(), center.y(), center.z()); - add_row_to_table(_u8L("Center") + ":", std::string(buf)); - sprintf(buf, "%.3f", radius); - add_row_to_table(_u8L("Radius") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); - add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + add_row_to_table(_u8L("Center") + ":", format_vec3(center)); + add_row_to_table(_u8L("Radius") + ":", format_double(radius)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); break; } case Measure::SurfaceFeatureType::Plane: @@ -507,11 +508,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", origin.x(), origin.y(), origin.z()); - add_row_to_table(_u8L("Origin") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); - add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); break; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 093584c7f4..8a8cdac755 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -49,9 +49,9 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; - bool m_mouse_left_down = false; // for detection left_up of this gizmo - const ModelObject* m_old_model_object = nullptr; - const ModelVolume* m_old_model_volume = nullptr; + bool m_mouse_left_down{ false }; // for detection left_up of this gizmo + const ModelObject* m_old_model_object{ nullptr }; + const ModelVolume* m_old_model_volume{ nullptr }; Vec2d m_mouse_pos{ Vec2d::Zero() }; From 83990562c5089248427056685b976c92e4c20fab Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 12:42:55 +0200 Subject: [PATCH 097/327] Measuring: Measure gizmo - added visualization of point for extended selection and updates to imgui dialog --- src/slic3r/GUI/GLCanvas3D.cpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 255 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 3 files changed, 151 insertions(+), 114 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9c98a20e7a..9adef7b472 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5465,8 +5465,13 @@ void GLCanvas3D::_picking_pass() // do not add the volume id if any gizmo is active and CTRL is pressed if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) { m_hover_volume_idxs.emplace_back(hit.raycaster_id); +#if !ENABLE_MEASURE_GIZMO m_gizmos.set_hover_id(-1); +#endif // !ENABLE_MEASURE_GIZMO } +#if ENABLE_MEASURE_GIZMO + m_gizmos.set_hover_id(-1); +#endif // ENABLE_MEASURE_GIZMO } } else @@ -5477,8 +5482,8 @@ void GLCanvas3D::_picking_pass() case SceneRaycaster::EType::Gizmo: { const Size& cnv_size = get_canvas_size(); - bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && - 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); + const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && + 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1); break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e803fa603..932f9113f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -21,7 +21,7 @@ std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType t { default: case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Point"); } + case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Vertex"); } case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } @@ -32,15 +32,22 @@ namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.2f, 0.8f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.8f, 0.8f, 0.2f, 1.0f }; static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; -static const int CIRCLE_CENTER_ID = 301; static const int PLANE_ID = 400; +static const std::string CTRL_STR = +#ifdef __APPLE__ +"⌘" +#else +"Ctrl" +#endif //__APPLE__ +; + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -127,8 +134,11 @@ bool GLGizmoMeasure::on_init() void GLGizmoMeasure::on_set_state() { - if (m_state == Off) + if (m_state == Off) { m_ctrl_kar_filter.reset_count(); + m_curr_feature.reset(); + m_curr_ex_feature_position.reset(); + } else m_mode = EMode::BasicSelection; } @@ -167,16 +177,19 @@ void GLGizmoMeasure::on_render() const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); - Vec3f pos; - Vec3f normal; + Vec3f position_on_model; + Vec3f normal_on_model; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &facet_idx); + + const bool is_hovering_on_extended_selection = m_mode == EMode::ExtendedSelection && m_hover_id != -1; std::optional curr_feature; if (m_mode == EMode::BasicSelection) - curr_feature = m_measuring->get_feature(facet_idx, pos.cast()); + curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); if (m_mode == EMode::BasicSelection) { + m_curr_ex_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); m_curr_feature = curr_feature; @@ -196,7 +209,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, n] = m_curr_feature->get_circle(); + const auto& [center, radius, normal] = m_curr_feature->get_circle(); // TODO: check for changed inv_zoom @@ -206,12 +219,12 @@ void GLGizmoMeasure::on_render() m_circle.model.init_from(std::move(circle_geometry)); m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, pt] = m_curr_feature->get_plane(); + const auto& [idx, normal, point] = m_curr_feature->get_plane(); // TODO: check for changed idx @@ -244,6 +257,8 @@ void GLGizmoMeasure::on_render() } } } + else if (m_mode == EMode::ExtendedSelection) + m_curr_ex_feature_position = position_on_model.cast(); if (!m_curr_feature.has_value()) return; @@ -261,23 +276,27 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); - ColorRGBA color; + ColorRGBA feature_color; switch (m_mode) { - case EMode::BasicSelection: { color = BASIC_HOVER_COLOR; break; } - case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } + case EMode::BasicSelection: { feature_color = BASIC_HOVER_COLOR; break; } + case EMode::ExtendedSelection: { feature_color = LOCK_COLOR; break; } } + auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + }; + switch (m_curr_feature->get_type()) { case Measure::SurfaceFeatureType::Point: { const Vec3d& position = m_curr_feature->get_point(); const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(color); + set_matrix_uniforms(feature_matrix); + m_sphere.model.set_color(is_hovering_on_extended_selection ? EXTENDED_HOVER_COLOR : feature_color); m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -289,23 +308,17 @@ void GLGizmoMeasure::on_render() const auto& [center, radius, n] = m_curr_feature->get_circle(); // render center const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d center_view_model_matrix = view_matrix * center_matrix; - shader->set_uniform("view_model_matrix", center_view_model_matrix); - const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(color); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color((is_hovering_on_extended_selection && m_hover_id == POINT_ID) ? EXTENDED_HOVER_COLOR : feature_color); m_sphere.model.render(); - auto it = m_raycasters.find(CIRCLE_CENTER_ID); + auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); // render circle const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; - shader->set_uniform("view_model_matrix", circle_view_model_matrix); - const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(color); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(feature_color); m_circle.model.render(); it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -318,11 +331,8 @@ void GLGizmoMeasure::on_render() auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(color); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(feature_color); m_cylinder.model.render(); auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -333,11 +343,8 @@ void GLGizmoMeasure::on_render() { const auto& [idx, normal, pt] = m_curr_feature->get_plane(); assert(idx < m_plane_models_cache.size()); - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(color); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(feature_color); m_plane_models_cache[idx].render(); auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -346,6 +353,15 @@ void GLGizmoMeasure::on_render() } } + if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { + if (m_hover_id != POINT_ID) { + Transform3d matrix = model_matrix * Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(matrix); + m_sphere.model.set_color(EXTENDED_HOVER_COLOR); + m_sphere.model.render(); + } + } + shader->stop_using(); } } @@ -450,70 +466,97 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - if (!m_curr_feature.has_value()) - m_imgui->text(_u8L("Select features to measure")); - else { - auto add_row_to_table = [this](const wxString& label, const std::string& value) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); - ImGui::TableSetColumnIndex(1); - m_imgui->text(value); - }; - auto format_double = [](double value) { - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); - }; - auto format_vec3 = [](const Vec3d& v) { - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); - }; + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; + auto format_double = [](double value) { + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); + }; + auto format_vec3 = [](const Vec3d& v) { + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); + }; + if (ImGui::BeginTable("Commands", 2)) { + add_row_to_table(_u8L("Left mouse button"), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + if (m_mode == EMode::BasicSelection && m_hover_id != -1) + add_row_to_table(CTRL_STR, _u8L("Enable point selection")); + ImGui::EndTable(); + } + + if (m_curr_feature.has_value()) { const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); - if (type != Measure::SurfaceFeatureType::Undef) { - if (ImGui::CollapsingHeader(surface_feature_type_as_string(type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - switch (type) - { - case Measure::SurfaceFeatureType::Point: - { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); + const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); + if (m_mode == EMode::BasicSelection) { + if (feature_type != Measure::SurfaceFeatureType::Undef) { + if (ImGui::CollapsingHeader(surface_feature_type_as_string(feature_type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("Data", 2)) { + switch (feature_type) + { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * m_curr_feature->get_point(); + add_row_to_table(_u8L("Position") + ":", format_vec3(position)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + add_row_to_table(_u8L("From") + ":", format_vec3(from)); + add_row_to_table(_u8L("To") + ":", format_vec3(to)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Center") + ":", format_vec3(center)); + add_row_to_table(_u8L("Radius") + ":", format_double(radius)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); + break; + } + } + ImGui::EndTable(); + } + } + } + } + else if (m_mode == EMode::ExtendedSelection) { + if (m_hover_id != -1) { + std::string header; + switch (feature_type) { + case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { header = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { header = (m_hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { header = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + + if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + const Vec3d position = volume_matrix * *m_curr_ex_feature_position; + if (ImGui::BeginTable("Data", 2)) { add_row_to_table(_u8L("Position") + ":", format_vec3(position)); - break; + ImGui::EndTable(); } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; - add_row_to_table(_u8L("From") + ":", format_vec3(from)); - add_row_to_table(_u8L("To") + ":", format_vec3(to)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Center") + ":", format_vec3(center)); - add_row_to_table(_u8L("Radius") + ":", format_double(radius)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - } - ImGui::EndTable(); } } } @@ -526,18 +569,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->set_requires_extra_frame(); } - if (m_curr_feature.has_value() && m_mode == EMode::BasicSelection) { - static const std::string ctrl = -#ifdef __APPLE__ - "⌘" -#else - "Ctrl" -#endif //__APPLE__ - ; - - ImGui::Separator(); - m_imgui->text(_u8L("Press") + " " + ctrl + " " + _u8L("to enable extended selection")); - } m_imgui->end(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 8a8cdac755..09d40b47ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -42,6 +42,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; + std::optional m_curr_ex_feature_position; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 7cf85a1565c790e885d886921d6cd165fb7bb45a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 14:56:00 +0200 Subject: [PATCH 098/327] Measuring: Measure gizmo - Improved visualization of points for extended selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 53 +++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 932f9113f1..89ba8cd410 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -257,8 +257,50 @@ void GLGizmoMeasure::on_render() } } } - else if (m_mode == EMode::ExtendedSelection) - m_curr_ex_feature_position = position_on_model.cast(); + else if (is_hovering_on_extended_selection) { + switch (m_curr_feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + m_curr_ex_feature_position = trafo * p.cast(); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + m_curr_ex_feature_position = model_matrix * position_on_model.cast(); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = m_curr_feature->get_circle(); + if (m_hover_id == POINT_ID) + m_curr_ex_feature_position = model_matrix * center; + else { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + m_curr_ex_feature_position = trafo * p.cast(); + } + } + break; + } + } + } if (!m_curr_feature.has_value()) return; @@ -355,7 +397,7 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - Transform3d matrix = model_matrix * Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); @@ -541,7 +583,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1) { + if (m_hover_id != -1 && m_curr_ex_feature_position.has_value()) { std::string header; switch (feature_type) { case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } @@ -552,9 +594,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - const Vec3d position = volume_matrix * *m_curr_ex_feature_position; if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position") + ":", format_vec3(position)); + add_row_to_table(_u8L("Position") + ":", format_vec3(*m_curr_ex_feature_position)); ImGui::EndTable(); } } From 58da6e994cd4c9eea2750760681f33c86c9279ea Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 15:31:39 +0200 Subject: [PATCH 099/327] Measuring: Measure gizmo - Further improvements in visualization of points for extended selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 89ba8cd410..a9c2e18257 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -273,6 +273,7 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + p = { 0.0f, 0.0f, p.z() }; m_curr_ex_feature_position = trafo * p.cast(); } break; @@ -294,6 +295,10 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + float angle = std::atan2(p.y(), p.x()); + if (angle < 0.0f) + angle += 2.0f * float(M_PI); + p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); m_curr_ex_feature_position = trafo * p.cast(); } } From ec9001c57e9eb4f0847ca0c58f7cd19b07b41707 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 09:44:40 +0200 Subject: [PATCH 100/327] Measuring: Optimization into GLGizmoMeasure::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 59 +++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a9c2e18257..13415e70ac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -106,6 +106,9 @@ void GLGizmoMeasure::data_changed() } if (model_object != m_old_model_object || model_volume != m_old_model_volume) update_if_needed(); + + m_last_inv_zoom = 0.0f; + m_last_plane_idx = -1; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -211,12 +214,13 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = m_curr_feature->get_circle(); - // TODO: check for changed inv_zoom - - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); + if (m_last_inv_zoom != inv_zoom) { + m_last_inv_zoom = inv_zoom; + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); + } m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); @@ -225,32 +229,33 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Plane: { const auto& [idx, normal, point] = m_curr_feature->get_plane(); + if (m_last_plane_idx != idx) { + m_last_plane_idx = idx; + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; - // TODO: check for changed idx + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); } - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 09d40b47ba..46b37737ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -44,11 +44,13 @@ class GLGizmoMeasure : public GLGizmoBase std::optional m_curr_feature; std::optional m_curr_ex_feature_position; - // This holds information to decide whether recalculation is necessary: + // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; + float m_last_inv_zoom{ 0.0f }; + int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo const ModelObject* m_old_model_object{ nullptr }; From fe7982cb08428aec915d3ab7262e7618e2cde2c8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 09:54:16 +0200 Subject: [PATCH 101/327] Fixed warnings --- src/libslic3r/Measure.cpp | 18 +++++++++--------- src/libslic3r/Measure.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index cdde276dda..1a79369a3e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -106,7 +106,7 @@ void MeasuringImpl::update_planes() int facet_idx = facet_queue[-- facet_queue_cnt]; const stl_normal& this_normal = face_normals[facet_idx]; if (is_same_normal(this_normal, *normal_ptr)) { - const Vec3i& face = m_its.indices[facet_idx]; +// const Vec3i& face = m_its.indices[facet_idx]; m_face_to_plane[facet_idx] = m_planes.size() - 1; m_planes.back().facets.emplace_back(facet_idx); @@ -132,7 +132,7 @@ void MeasuringImpl::update_planes() for (int face_id=0; face_id lengths; - for (int i=0; i M_PI) @@ -236,10 +236,10 @@ void MeasuringImpl::extract_features() bool circle = false; std::vector circles; std::vector> circles_idxs; - for (int i=1; i polygon_lower_threshold) { if (angle < polygon_upper_threshold) { const Vec3d center = std::get<0>(circles[i].get_circle()); - for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) + for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[j-1], border[j], std::make_optional(center), 0.)); } else { @@ -286,7 +286,7 @@ void MeasuringImpl::extract_features() // We have the circles. Now go around again and pick edges. int cidx = 0; // index of next circle in the way for (int i=1; i circles_idxs[cidx].first) + if (cidx < (int)circles_idxs.size() && i > (int)circles_idxs[cidx].first) i = circles_idxs[cidx++].second; else plane.surface_features.emplace_back(SurfaceFeature( SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 95d8f34cba..9e3629589b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -68,7 +68,7 @@ private: Vec3d m_pt1; Vec3d m_pt2; std::optional m_pt3; - double m_value; + double m_value{ 0.0 }; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 13415e70ac..54c944590b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -200,6 +200,7 @@ void GLGizmoMeasure::on_render() return; switch (m_curr_feature->get_type()) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); @@ -265,6 +266,7 @@ void GLGizmoMeasure::on_render() else if (is_hovering_on_extended_selection) { switch (m_curr_feature->get_type()) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); @@ -342,7 +344,9 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - switch (m_curr_feature->get_type()) { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d& position = m_curr_feature->get_point(); @@ -552,6 +556,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Data", 2)) { switch (feature_type) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d position = volume_matrix * m_curr_feature->get_point(); From c6e6c86627f2889253425787fedaea08c1d2f850 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 10:19:12 +0200 Subject: [PATCH 102/327] Refactoring into GLGizmoMeasure.cpp to remove duplicated code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 63 +++++++++++------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 54c944590b..3b257076a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -48,6 +48,30 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) +{ + assert(0 <= idx && idx < (int)planes_triangles.size()); + const std::vector& triangle_indices = planes_triangles[idx]; + + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + return init_data; +} + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -232,26 +256,9 @@ void GLGizmoMeasure::on_render() const auto& [idx, normal, point] = m_curr_feature->get_plane(); if (m_last_plane_idx != idx) { m_last_plane_idx = idx; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } - + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane.reset(); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); m_plane.model.init_from(std::move(init_data)); @@ -427,23 +434,9 @@ void GLGizmoMeasure::update_if_needed() auto update_plane_models_cache = [this](const indexed_triangle_set& its) { m_plane_models_cache.clear(); const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - for (const std::vector& triangle_indices : planes_triangles) { + for (int idx = 0; idx < (int)planes_triangles.size(); ++idx) { m_plane_models_cache.emplace_back(GLModel()); - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane_models_cache.back().init_from(std::move(init_data)); } }; From c5532b175ed894428eec232b90d00bd3a21c59f8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 2 Sep 2022 11:24:06 +0200 Subject: [PATCH 103/327] Measuring: Added features selection in GLGizmoMeasure --- src/libslic3r/Measure.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 382 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 39 ++- 3 files changed, 297 insertions(+), 130 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 9e3629589b..3aefc0b54b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -107,7 +107,11 @@ struct MeasurementResult { std::optional angle; std::optional distance_infinite; std::optional distance_strict; - std::optional distance_xyz; + std::optional distance_xyz; + + bool has_any_data() const { + return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); + } }; // Returns distance/angle between two SurfaceFeatures. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3b257076a2..8c27678ce0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -31,9 +31,8 @@ std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType t namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.8f, 0.8f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; static const int POINT_ID = 100; static const int EDGE_ID = 200; @@ -95,7 +94,23 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; + m_mouse_left_down = true; + + auto set_item_from_feature = [this]() { + const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_ex_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : + m_curr_feature }; + return item; + }; + + if (m_selected_features.first.feature.has_value()) { + const SelectedFeatures::Item item = set_item_from_feature(); + if (m_selected_features.first != item) + m_selected_features.second = item; + } + else + m_selected_features.first = set_item_from_feature(); + return true; } @@ -103,7 +118,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) // take responsibility for left up if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; - } else if (mouse_event.LeftUp()) { if (m_mouse_left_down) { @@ -133,6 +147,7 @@ void GLGizmoMeasure::data_changed() m_last_inv_zoom = 0.0f; m_last_plane_idx = -1; + m_selected_features.reset(); } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -321,13 +336,13 @@ void GLGizmoMeasure::on_render() } } - if (!m_curr_feature.has_value()) + if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) return; GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; - + shader->start_using(); shader->set_uniform("emission_factor", 0.25f); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); @@ -337,13 +352,6 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); - ColorRGBA feature_color; - switch (m_mode) - { - case EMode::BasicSelection: { feature_color = BASIC_HOVER_COLOR; break; } - case EMode::ExtendedSelection: { feature_color = LOCK_COLOR; break; } - } - auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { const Transform3d view_model_matrix = view_matrix * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); @@ -351,76 +359,143 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - switch (m_curr_feature->get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = m_curr_feature->get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - m_sphere.model.set_color(is_hovering_on_extended_selection ? EXTENDED_HOVER_COLOR : feature_color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = m_curr_feature->get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color((is_hovering_on_extended_selection && m_hover_id == POINT_ID) ? EXTENDED_HOVER_COLOR : feature_color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); + auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, + const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = feature.get_edge(); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(colors.front()); + m_cylinder.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + } + break; + } + } + }; - // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(feature_color); - m_circle.model.render(); - it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - break; + auto hover_selection_color = [this]() { + return saturate(!m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR, 1.5f); + }; + + auto hovering_color = [this, hover_selection_color, &selection]() { + return (m_mode == EMode::ExtendedSelection) ? selection.get_first_volume()->render_color : hover_selection_color(); + }; + + if (m_curr_feature.has_value()) { + std::vector colors; + if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature) + colors.emplace_back(SELECTED_1ST_COLOR); + else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature) + colors.emplace_back(SELECTED_2ND_COLOR); + else { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + colors.emplace_back(hover_selection_color()); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + colors.emplace_back((m_hover_id == POINT_ID) ? hover_selection_color() : hovering_color()); + colors.emplace_back(hovering_color()); + break; + } + case Measure::SurfaceFeatureType::Edge: + case Measure::SurfaceFeatureType::Plane: + { + colors.emplace_back(hovering_color()); + break; + } + } + } + + render_feature(*m_curr_feature, colors, model_matrix, inv_zoom, true); } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = m_curr_feature->get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(feature_color); - m_cylinder.model.render(); - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = m_curr_feature->get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); - m_plane_models_cache[idx].set_color(feature_color); - m_plane_models_cache[idx].render(); - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); - break; + + if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { + std::vector colors; + colors.emplace_back(SELECTED_1ST_COLOR); + render_feature(*m_selected_features.first.feature, colors, + (m_selected_features.first.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } + if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { + std::vector colors; + colors.emplace_back(SELECTED_2ND_COLOR); + render_feature(*m_selected_features.second.feature, colors, + (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); - m_sphere.model.set_color(EXTENDED_HOVER_COLOR); + m_sphere.model.set_color(hover_selection_color()); +// m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); } } @@ -496,6 +571,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit { static std::optional last_feature; static EMode last_mode = EMode::BasicSelection; + static SelectedFeatures last_selected_features; static float last_y = 0.0f; static float last_h = 0.0f; @@ -515,18 +591,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const wxString& label, const std::string& value) { + auto add_row_to_table = [this](const std::string& label, const ImVec4& label_color, const std::string& value, const ImVec4& value_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + m_imgui->text_colored(label_color, label); ImGui::TableSetColumnIndex(1); - m_imgui->text(value); + m_imgui->text_colored(value_color, value); }; + auto format_double = [](double value) { char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); }; + auto format_vec3 = [](const Vec3d& v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); @@ -534,9 +612,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (ImGui::BeginTable("Commands", 2)) { - add_row_to_table(_u8L("Left mouse button"), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + add_row_to_table(_u8L("Left mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, + (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_row_to_table(CTRL_STR, _u8L("Enable point selection")); + add_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -545,48 +624,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); if (m_mode == EMode::BasicSelection) { if (feature_type != Measure::SurfaceFeatureType::Undef) { - if (ImGui::CollapsingHeader(surface_feature_type_as_string(feature_type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - switch (feature_type) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); - add_row_to_table(_u8L("Position") + ":", format_vec3(position)); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; - add_row_to_table(_u8L("From") + ":", format_vec3(from)); - add_row_to_table(_u8L("To") + ":", format_vec3(to)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Center") + ":", format_vec3(center)); - add_row_to_table(_u8L("Radius") + ":", format_double(radius)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - } - ImGui::EndTable(); + ImGui::Separator(); + m_imgui->text(surface_feature_type_as_string(feature_type) + ":"); + if (ImGui::BeginTable("Data", 2)) { + switch (feature_type) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * m_curr_feature->get_point(); + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Length") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Radius") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + ImGui::EndTable(); } } } @@ -601,20 +681,68 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } } - if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position") + ":", format_vec3(*m_curr_ex_feature_position)); - ImGui::EndTable(); - } + ImGui::Separator(); + m_imgui->text(header + ":"); + if (ImGui::BeginTable("Data", 2)) { + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_ex_feature_position), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); } } } } - if (last_feature != m_curr_feature || last_mode != m_mode) { + ImGui::Separator(); + m_imgui->text(_u8L("Selection")); + if (ImGui::BeginTable("Selection", 2)) { + add_row_to_table("1", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + surface_feature_type_as_string(m_selected_features.first.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + add_row_to_table("2", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + surface_feature_type_as_string(m_selected_features.second.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + ImGui::EndTable(); + } + + if (m_selected_features.first.feature.has_value()) { + if (m_imgui->button(_u8L("Restart"))) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); + } + } + + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + ImGui::Separator(); + if (measure.has_any_data()) { + m_imgui->text(_u8L("Measure")); + if (ImGui::BeginTable("Measure", 2)) { + if (measure.angle.has_value()) { + add_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_infinite.has_value()) { + add_row_to_table(_u8L("Distance Infinite") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_infinite), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_strict.has_value()) { + add_row_to_table(_u8L("Distance Strict") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_strict), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_xyz.has_value()) { + add_row_to_table(_u8L("Distance XYZ") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*measure.distance_xyz), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + } + else + m_imgui->text(_u8L("No measure available")); + } + + if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { // the dialog may have changed its size, ask for an extra frame to render it properly last_feature = m_curr_feature; last_mode = m_mode; + last_selected_features = m_selected_features; m_imgui->set_requires_extra_frame(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 46b37737ae..9e0cbd946c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,14 +23,47 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoMeasure : public GLGizmoBase { -// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. - enum class EMode : unsigned char { BasicSelection, ExtendedSelection }; + struct SelectedFeatures + { + struct Item + { + EMode mode{ EMode::BasicSelection }; + std::optional feature; + + bool operator == (const Item& other) const { + if (this->mode != other.mode) return false; + return this->feature == other.feature; + } + + bool operator != (const Item& other) const { + return !operator == (other); + } + }; + + Item first; + Item second; + + void reset() { + first.feature.reset(); + second.feature.reset(); + } + + bool operator == (const SelectedFeatures& other) const { + if (this->first != other.first) return false; + return this->second == other.second; + } + + bool operator != (const SelectedFeatures& other) const { + return !operator == (other); + } + }; + EMode m_mode{ EMode::BasicSelection }; std::unique_ptr m_measuring; @@ -60,6 +93,8 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; + SelectedFeatures m_selected_features; + void update_if_needed(); public: From 5e271a18df789de7af53749b767a9587c726685b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 2 Sep 2022 12:26:56 +0200 Subject: [PATCH 104/327] Measuring: Changes in GLGizmoMeasure imgui dialog layout --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 97 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 14 +++- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8c27678ce0..6fe6c36ef3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -15,19 +15,6 @@ #if ENABLE_MEASURE_GIZMO -std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType type) -{ - switch (type) - { - default: - case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Vertex"); } - case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } - case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } - case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } - } -} - namespace Slic3r { namespace GUI { @@ -47,6 +34,32 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } + case Measure::SurfaceFeatureType::Point: { return L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + } +} + +static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id) +{ + std::string ret; + switch (type) { + case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + return ret; +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -97,9 +110,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto set_item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_ex_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : - m_curr_feature }; + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_point_on_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : m_curr_feature }; return item; }; @@ -179,7 +192,7 @@ void GLGizmoMeasure::on_set_state() if (m_state == Off) { m_ctrl_kar_filter.reset_count(); m_curr_feature.reset(); - m_curr_ex_feature_position.reset(); + m_curr_point_on_feature_position.reset(); } else m_mode = EMode::BasicSelection; @@ -231,7 +244,7 @@ void GLGizmoMeasure::on_render() curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); if (m_mode == EMode::BasicSelection) { - m_curr_ex_feature_position.reset(); + m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); m_curr_feature = curr_feature; @@ -291,7 +304,7 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); + m_curr_point_on_feature_position = model_matrix * m_curr_feature->get_point(); break; } case Measure::SurfaceFeatureType::Edge: @@ -303,20 +316,20 @@ void GLGizmoMeasure::on_render() const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); p = { 0.0f, 0.0f, p.z() }; - m_curr_ex_feature_position = trafo * p.cast(); + m_curr_point_on_feature_position = trafo * p.cast(); } break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_ex_feature_position = model_matrix * position_on_model.cast(); + m_curr_point_on_feature_position = model_matrix * position_on_model.cast(); break; } case Measure::SurfaceFeatureType::Circle: { const auto& [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) - m_curr_ex_feature_position = model_matrix * center; + m_curr_point_on_feature_position = model_matrix * center; else { auto it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) { @@ -328,7 +341,7 @@ void GLGizmoMeasure::on_render() if (angle < 0.0f) angle += 2.0f * float(M_PI); p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); - m_curr_ex_feature_position = trafo * p.cast(); + m_curr_point_on_feature_position = trafo * p.cast(); } } break; @@ -490,12 +503,11 @@ void GLGizmoMeasure::on_render() (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } - if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { + if (is_hovering_on_extended_selection && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); -// m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); } } @@ -591,12 +603,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const std::string& label, const ImVec4& label_color, const std::string& value, const ImVec4& value_color) { + auto add_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(label_color, label); + m_imgui->text_colored(col_1_color, col_1); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(value_color, value); + m_imgui->text_colored(col_2_color, col_2); }; auto format_double = [](double value) { @@ -671,20 +683,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1 && m_curr_ex_feature_position.has_value()) { - std::string header; - switch (feature_type) { - case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { header = _u8L("Point on edge"); break; } - case Measure::SurfaceFeatureType::Circle: { header = (m_hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } - case Measure::SurfaceFeatureType::Plane: { header = _u8L("Point on plane"); break; } - default: { assert(false); break; } - } - + if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { ImGui::Separator(); - m_imgui->text(header + ":"); + m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_ex_feature_position), + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_point_on_feature_position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -693,12 +696,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ImGui::Separator(); - m_imgui->text(_u8L("Selection")); - if (ImGui::BeginTable("Selection", 2)) { - add_row_to_table("1", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? - surface_feature_type_as_string(m_selected_features.first.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_row_to_table("2", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? - surface_feature_type_as_string(m_selected_features.second.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + const ImGuiTableFlags flags = /*ImGuiTableFlags_SizingStretchSame | */ImGuiTableFlags_BordersOuter | /*ImGuiTableFlags_BordersV | */ImGuiTableFlags_BordersH; + if (ImGui::BeginTable("Selection", 2, flags)) { + add_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + add_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9e0cbd946c..5755d9a912 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -34,24 +34,32 @@ class GLGizmoMeasure : public GLGizmoBase struct Item { EMode mode{ EMode::BasicSelection }; + std::string source; std::optional feature; bool operator == (const Item& other) const { if (this->mode != other.mode) return false; + if (this->source != other.source) return false; return this->feature == other.feature; } bool operator != (const Item& other) const { return !operator == (other); } + + void reset() { + mode = EMode::BasicSelection; + source.clear(); + feature.reset(); + } }; Item first; Item second; void reset() { - first.feature.reset(); - second.feature.reset(); + first.reset(); + second.reset(); } bool operator == (const SelectedFeatures& other) const { @@ -75,7 +83,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; - std::optional m_curr_ex_feature_position; + std::optional m_curr_point_on_feature_position; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 91266e48158ed1c7e47a18eab0c7aaa4551838bd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 08:26:19 +0200 Subject: [PATCH 105/327] Measuring: Disable scene raycasters while GLGizmoMeasure is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 101 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 ++ 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6fe6c36ef3..8fddf11adf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -167,8 +167,10 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po { if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (m_curr_feature.has_value()) + if (m_curr_feature.has_value()) { m_mode = EMode::ExtendedSelection; + disable_scene_raycasters(); + } } m_ctrl_kar_filter.increase_count(); @@ -176,6 +178,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po else if (action == SLAGizmoEventType::CtrlUp) { m_ctrl_kar_filter.reset_count(); m_mode = EMode::BasicSelection; + restore_scene_raycasters_state(); } return true; @@ -193,9 +196,19 @@ void GLGizmoMeasure::on_set_state() m_ctrl_kar_filter.reset_count(); m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); + restore_scene_raycasters_state(); } - else + else { m_mode = EMode::BasicSelection; + // store current state of scene raycaster for later use + m_scene_raycaster_state.clear(); + m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (m_scene_raycasters != nullptr) { + for (const auto r : *m_scene_raycasters) { + m_scene_raycaster_state.emplace_back(r->is_active()); + } + } + } } CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const @@ -234,16 +247,13 @@ void GLGizmoMeasure::on_render() Vec3f position_on_model; Vec3f normal_on_model; - size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &facet_idx); + size_t model_facet_idx; + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - const bool is_hovering_on_extended_selection = m_mode == EMode::ExtendedSelection && m_hover_id != -1; - - std::optional curr_feature; - if (m_mode == EMode::BasicSelection) - curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); + const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { + std::optional curr_feature = m_measuring->get_feature(model_facet_idx, position_on_model.cast()); m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); @@ -265,8 +275,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, normal] = m_curr_feature->get_circle(); - + const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_last_inv_zoom != inv_zoom) { m_last_inv_zoom = inv_zoom; m_circle.reset(); @@ -281,7 +290,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, point] = m_curr_feature->get_plane(); + const auto [idx, normal, point] = m_curr_feature->get_plane(); if (m_last_plane_idx != idx) { m_last_plane_idx = idx; const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; @@ -298,7 +307,23 @@ void GLGizmoMeasure::on_render() } } } - else if (is_hovering_on_extended_selection) { + else if (is_hovering_on_locked_feature) { + auto default_callback = [](const Vec3f& v) { return v; }; + auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { + auto it = m_raycasters.find(feature_type_id); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); + assert(res); + if (callback) + p = callback(p); + return trafo * p.cast(); + } + return Vec3d::Zero(); + }; + switch (m_curr_feature->get_type()) { default: { assert(false); break; } @@ -309,40 +334,27 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) { - Vec3f p; - Vec3f n; - const Transform3d& trafo = it->second->get_transform(); - it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); - p = { 0.0f, 0.0f, p.z() }; - m_curr_point_on_feature_position = trafo * p.cast(); - } + m_curr_point_on_feature_position = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = model_matrix * position_on_model.cast(); + m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); break; } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, normal] = m_curr_feature->get_circle(); + const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) m_curr_point_on_feature_position = model_matrix * center; else { - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) { - Vec3f p; - Vec3f n; - const Transform3d& trafo = it->second->get_transform(); - it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); - float angle = std::atan2(p.y(), p.x()); + const float r = radius; // needed for the following lambda + m_curr_point_on_feature_position = position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { + float angle = std::atan2(v.y(), v.x()); if (angle < 0.0f) angle += 2.0f * float(M_PI); - p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); - m_curr_point_on_feature_position = trafo * p.cast(); - } + return float(r) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); + }); } break; } @@ -503,7 +515,7 @@ void GLGizmoMeasure::on_render() (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } - if (is_hovering_on_extended_selection && m_curr_point_on_feature_position.has_value()) { + if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); @@ -579,6 +591,25 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::disable_scene_raycasters() +{ + if (m_scene_raycasters != nullptr) { + for (auto r : *m_scene_raycasters) { + r->set_active(false); + } + } +} + +void GLGizmoMeasure::restore_scene_raycasters_state() +{ + if (m_scene_raycasters != nullptr) { + assert(m_scene_raycasters->size() == m_scene_raycaster_state.size()); + for (size_t i = 0; i < m_scene_raycasters->size(); ++i) { + (*m_scene_raycasters)[i]->set_active(m_scene_raycaster_state[i]); + } + } +} + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 5755d9a912..3bbeb92b44 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -84,6 +84,8 @@ class GLGizmoMeasure : public GLGizmoBase std::map> m_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; + std::vector>* m_scene_raycasters{ nullptr }; + std::vector m_scene_raycaster_state; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -105,6 +107,9 @@ class GLGizmoMeasure : public GLGizmoBase void update_if_needed(); + void disable_scene_raycasters(); + void restore_scene_raycasters_state(); + public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From ae3beeb50a60631f4c6c67162d709d3ee34506a7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 08:57:49 +0200 Subject: [PATCH 106/327] Fixed warnings --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8fddf11adf..74a5c93f7c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -308,7 +308,6 @@ void GLGizmoMeasure::on_render() } } else if (is_hovering_on_locked_feature) { - auto default_callback = [](const Vec3f& v) { return v; }; auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { auto it = m_raycasters.find(feature_type_id); if (it != m_raycasters.end() && it->second != nullptr) { @@ -317,9 +316,11 @@ void GLGizmoMeasure::on_render() const Transform3d& trafo = it->second->get_transform(); bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); assert(res); - if (callback) - p = callback(p); - return trafo * p.cast(); + if (res) { + if (callback) + p = callback(p); + return trafo * p.cast(); + } } return Vec3d::Zero(); }; From 8050d457d38cd1250cb529a5ad17d845ef7fdc68 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 09:18:07 +0200 Subject: [PATCH 107/327] Measuring: GLGizmoMeasure - Fixed update of circle geometry --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 74a5c93f7c..bf7fd04a7d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -276,8 +276,9 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Circle: { const auto [center, radius, normal] = m_curr_feature->get_circle(); - if (m_last_inv_zoom != inv_zoom) { + if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { m_last_inv_zoom = inv_zoom; + m_last_circle = m_curr_feature; m_circle.reset(); GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 3bbeb92b44..d74fb9ed8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -93,6 +93,7 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; float m_last_inv_zoom{ 0.0f }; + std::optional m_last_circle; int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo From bac3eebf51e5fdb5cd189aff06d2ad7678562b08 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 12:05:25 +0200 Subject: [PATCH 108/327] Measuring: GLGizmoMeasure - show data in inches into imgui dialog, when needed --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bf7fd04a7d..f8f84d7bc8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -3,6 +3,7 @@ #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" @@ -664,6 +665,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::EndTable(); } + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); + if (m_curr_feature.has_value()) { const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); @@ -677,7 +681,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); + Vec3d position = volume_matrix * m_curr_feature->get_point(); + if (use_inches) + position = ObjectManipulation::mm_to_in * position; add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -686,9 +692,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; + if (use_inches) { + from = ObjectManipulation::mm_to_in * from; + to = ObjectManipulation::mm_to_in * to; + } add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Length") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -696,8 +706,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) { + center = ObjectManipulation::mm_to_in * center; + radius = ObjectManipulation::mm_to_in * radius; + } add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Radius") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -706,6 +720,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) + origin = ObjectManipulation::mm_to_in * origin; add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; @@ -720,7 +736,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_point_on_feature_position), + Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -756,15 +775,24 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { - add_row_to_table(_u8L("Distance Infinite") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_infinite), + double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { - add_row_to_table(_u8L("Distance Strict") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_strict), + double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { - add_row_to_table(_u8L("Distance XYZ") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*measure.distance_xyz), + Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); From 8a88b98f7f32fa9c276e45ce6edb7f599d3d3171 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 12:49:19 +0200 Subject: [PATCH 109/327] Measuring: GLGizmoMeasure - Fixed detection of current hovered feature --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f8f84d7bc8..40ca6d3df3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -249,12 +249,11 @@ void GLGizmoMeasure::on_render() Vec3f position_on_model; Vec3f normal_on_model; size_t model_facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - + const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { - std::optional curr_feature = m_measuring->get_feature(model_facet_idx, position_on_model.cast()); + std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); @@ -552,7 +551,7 @@ void GLGizmoMeasure::update_if_needed() // Let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); - m_first_instance_scale = Vec3d::Ones(); + m_first_instance_scale = Vec3d::Ones(); m_first_instance_mirror = Vec3d::Ones(); if (object != nullptr) { for (const ModelVolume* vol : object->volumes) { From b788628a18929bd20b00279de869b01520af7fe2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 13:26:01 +0200 Subject: [PATCH 110/327] Fixed warnings on ARM64 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 40ca6d3df3..c1e81ff21d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,11 +89,11 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen : GLGizmoBase(parent, icon_filename, sprite_id) { GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); - m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(sphere_geometry.get_as_indexed_triangle_set()))); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(sphere_geometry.get_as_indexed_triangle_set())); m_sphere.model.init_from(std::move(sphere_geometry)); GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); - m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(cylinder_geometry.get_as_indexed_triangle_set()))); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(cylinder_geometry.get_as_indexed_triangle_set())); m_cylinder.model.init_from(std::move(cylinder_geometry)); } @@ -205,7 +205,7 @@ void GLGizmoMeasure::on_set_state() m_scene_raycaster_state.clear(); m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); if (m_scene_raycasters != nullptr) { - for (const auto r : *m_scene_raycasters) { + for (const auto& r : *m_scene_raycasters) { m_scene_raycaster_state.emplace_back(r->is_active()); } } @@ -281,7 +281,7 @@ void GLGizmoMeasure::on_render() m_last_circle = m_curr_feature; m_circle.reset(); GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); m_circle.model.init_from(std::move(circle_geometry)); } @@ -298,7 +298,7 @@ void GLGizmoMeasure::on_render() const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); m_plane.model.init_from(std::move(init_data)); } From cb57b3c5cb4bd8e849f41dee04e52a82ed007288 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 15:16:31 +0200 Subject: [PATCH 111/327] Measuring: GLGizmoMeasure - Added colored icon into imgui dialog --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 68 +++++++++++++++--------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c1e81ff21d..21e86532a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -636,12 +636,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + auto add_row_to_table = [this](std::function col_1 = nullptr, std::function col_2 = nullptr) { + assert(col_1 != nullptr && col_2 != nullptr); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(col_1_color, col_1); + col_1(); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(col_2_color, col_2); + col_2(); + }; + + auto add_strings_row_to_table = [this, add_row_to_table](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + add_row_to_table( + [this, &col_1, &col_1_color]() { m_imgui->text_colored(col_1_color, col_1); }, + [this, &col_2, &col_2_color]() { m_imgui->text_colored(col_2_color, col_2); } + ); }; auto format_double = [](double value) { @@ -657,10 +665,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (ImGui::BeginTable("Commands", 2)) { - add_row_to_table(_u8L("Left mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, - (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table( + [this]() { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); + }, + [this]() { + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + ImGui::SameLine(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + const float rect_size = ImGui::GetTextLineHeight(); + ImGui::GetWindowDrawList()->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + rect_size, pos.y + rect_size }, + ImGuiWrapper::to_ImU32(m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR)); + ImGui::Dummy(ImVec2(rect_size, rect_size)); + } + ); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -683,7 +703,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d position = volume_matrix * m_curr_feature->get_point(); if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: @@ -695,9 +715,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit from = ObjectManipulation::mm_to_in * from; to = ObjectManipulation::mm_to_in * to; } - add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -709,9 +729,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; } - add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Plane: @@ -721,8 +741,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) origin = ObjectManipulation::mm_to_in * origin; - add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } } @@ -738,7 +758,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), + add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -747,11 +767,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ImGui::Separator(); - const ImGuiTableFlags flags = /*ImGuiTableFlags_SizingStretchSame | */ImGuiTableFlags_BordersOuter | /*ImGuiTableFlags_BordersV | */ImGuiTableFlags_BordersH; + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; if (ImGui::BeginTable("Selection", 2, flags)) { - add_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + add_strings_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + add_strings_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } @@ -767,31 +787,31 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); ImGui::Separator(); if (measure.has_any_data()) { - m_imgui->text(_u8L("Measure")); + m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 2)) { if (measure.angle.has_value()) { - add_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_strings_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_strings_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); From 49885b9c6f8948c723ff8c6f93dc2b8388f6a3ae Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 10:17:53 +0200 Subject: [PATCH 112/327] Measuring: Added missing default values to SurfaceFeature member variables --- src/libslic3r/Measure.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 3aefc0b54b..3b811db0c5 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -64,9 +64,9 @@ public: } private: - SurfaceFeatureType m_type = SurfaceFeatureType::Undef; - Vec3d m_pt1; - Vec3d m_pt2; + SurfaceFeatureType m_type{ SurfaceFeatureType::Undef }; + Vec3d m_pt1{ Vec3d::Zero() }; + Vec3d m_pt2{ Vec3d::Zero() }; std::optional m_pt3; double m_value{ 0.0 }; }; From 80e1d8298b0feb335aa2d5752ca74df4076484e5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 10:54:56 +0200 Subject: [PATCH 113/327] Measuring: Rewritten method SurfaceFeature::operator ==() --- src/libslic3r/Measure.hpp | 23 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 3b811db0c5..5d71983f0b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -44,19 +44,28 @@ public: std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. - std::tuple get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); } + std::tuple get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); } // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } bool operator == (const SurfaceFeature& other) const { if (this->m_type != other.m_type) return false; - if (!this->m_pt1.isApprox(other.m_pt1)) return false; - if (!this->m_pt2.isApprox(other.m_pt2)) return false; - if (this->m_pt3.has_value() && !other.m_pt3.has_value()) return false; - if (!this->m_pt3.has_value() && other.m_pt3.has_value()) return false; - if (this->m_pt3.has_value() && other.m_pt3.has_value() && !(*this->m_pt3).isApprox(*other.m_pt3)) return false; - return this->m_value == other.m_value; + switch (this->m_type) + { + case SurfaceFeatureType::Undef: { break; } + case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); } + case SurfaceFeatureType::Edge: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) || + (this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1)); + } + case SurfaceFeatureType::Plane: + case SurfaceFeatureType::Circle: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON); + } + } + + return false; } bool operator != (const SurfaceFeature& other) const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 21e86532a1..942a8c93eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -113,7 +113,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) auto set_item_from_feature = [this]() { const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_point_on_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : m_curr_feature }; + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; return item; }; @@ -331,7 +331,7 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_point_on_feature_position = model_matrix * m_curr_feature->get_point(); + m_curr_point_on_feature_position = position_on_feature(POINT_ID, camera); break; } case Measure::SurfaceFeatureType::Edge: From 2b7520dc9eee8063c7d376811603c2d09c4a75e4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 12:02:44 +0200 Subject: [PATCH 114/327] Measuring: GLGizmoMeasure - Use mouse right click to restart selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 49 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 942a8c93eb..ebf3139868 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -110,20 +110,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_hover_id != -1) { m_mouse_left_down = true; - auto set_item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; - return item; - }; - if (m_selected_features.first.feature.has_value()) { - const SelectedFeatures::Item item = set_item_from_feature(); + const SelectedFeatures::Item item = item_from_feature(); if (m_selected_features.first != item) m_selected_features.second = item; } else - m_selected_features.first = set_item_from_feature(); + m_selected_features.first = item_from_feature(); return true; } @@ -140,6 +133,16 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return true; } } + else if (mouse_event.RightDown()) { + if (m_hover_id != -1) { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first == item_from_feature()) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); + } + } + } + } else if (mouse_event.Leaving()) m_mouse_left_down = false; @@ -612,6 +615,14 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +GLGizmoMeasure::SelectedFeatures::Item GLGizmoMeasure::item_from_feature() const +{ + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + return item; +} + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -679,6 +690,14 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Dummy(ImVec2(rect_size, rect_size)); } ); + + if (m_hover_id != -1) { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first == item_from_feature()) + add_strings_row_to_table(_u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + } + if (m_mode == EMode::BasicSelection && m_hover_id != -1) add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); @@ -776,12 +795,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::EndTable(); } - if (m_selected_features.first.feature.has_value()) { - if (m_imgui->button(_u8L("Restart"))) { - m_selected_features.reset(); - m_imgui->set_requires_extra_frame(); - } - } + //if (m_selected_features.first.feature.has_value()) { + // if (m_imgui->button(_u8L("Restart"))) { + // m_selected_features.reset(); + // m_imgui->set_requires_extra_frame(); + // } + //} if (m_selected_features.second.feature.has_value()) { const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index d74fb9ed8e..ccd542b86a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -110,6 +110,7 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); + SelectedFeatures::Item item_from_feature() const; public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 67c004498c0a38bb89466007bf273d01178a2c7e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 15:06:56 +0200 Subject: [PATCH 115/327] Follow-up of 2b7520dc9eee8063c7d376811603c2d09c4a75e4 - Use CTRL + mouse right click to restart selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 - 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index ebf3139868..f1a011e572 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -109,6 +109,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { m_mouse_left_down = true; + + auto item_from_feature = [this]() { + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + return item; + }; if (m_selected_features.first.feature.has_value()) { const SelectedFeatures::Item item = item_from_feature(); @@ -133,15 +140,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return true; } } - else if (mouse_event.RightDown()) { - if (m_hover_id != -1) { - if (m_selected_features.first.feature.has_value()) { - if (m_selected_features.first == item_from_feature()) { - m_selected_features.reset(); - m_imgui->set_requires_extra_frame(); - } - } - } + else if (mouse_event.RightDown() && mouse_event.CmdDown()) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); } else if (mouse_event.Leaving()) m_mouse_left_down = false; @@ -615,14 +616,6 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } -GLGizmoMeasure::SelectedFeatures::Item GLGizmoMeasure::item_from_feature() const -{ - const SelectedFeatures::Item item = { m_mode, - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; - return item; -} - void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -691,12 +684,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); - if (m_hover_id != -1) { - if (m_selected_features.first.feature.has_value()) { - if (m_selected_features.first == item_from_feature()) - add_strings_row_to_table(_u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - } - } + if (m_selected_features.first.feature.has_value()) + add_strings_row_to_table(CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index ccd542b86a..d74fb9ed8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -110,7 +110,6 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); - SelectedFeatures::Item item_from_feature() const; public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 2dd67745a5086b72e2f7ea8b0d31e129247e6378 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Sep 2022 13:07:15 +0200 Subject: [PATCH 116/327] Measuring: bunch of fixes into GLGizmoMeasure + new tech ENABLE_MEASURE_GIZMO_DEBUG to show a debug imgui dialog containing data related to Measure Gizmo --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 222 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 12 +- 3 files changed, 152 insertions(+), 83 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ebb469e509..903612f29b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,6 +78,7 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f1a011e572..b434cea9a7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -111,7 +111,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, + const SelectedFeatures::Item item = { (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; return item; @@ -236,6 +236,10 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { +#if ENABLE_MEASURE_GIZMO_DEBUG + render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; @@ -246,14 +250,14 @@ void GLGizmoMeasure::on_render() (selection.is_single_volume() || selection.is_single_volume_instance())) { update_if_needed(); - const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); + m_volume_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); Vec3f position_on_model; Vec3f normal_on_model; size_t model_facet_idx; - const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); + const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { @@ -335,31 +339,31 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_point_on_feature_position = position_on_feature(POINT_ID, camera); + m_curr_point_on_feature_position = m_curr_feature->get_point(); break; } case Measure::SurfaceFeatureType::Edge: { - m_curr_point_on_feature_position = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera); break; } case Measure::SurfaceFeatureType::Circle: { const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) - m_curr_point_on_feature_position = model_matrix * center; + m_curr_point_on_feature_position = center; else { const float r = radius; // needed for the following lambda - m_curr_point_on_feature_position = position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { float angle = std::atan2(v.y(), v.x()); if (angle < 0.0f) angle += 2.0f * float(M_PI); - return float(r) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); + return Vec3f(float(r) * std::cos(angle), float(r) * std::sin(angle), 0.0f); }); } break; @@ -411,7 +415,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, n] = feature.get_circle(); + const auto& [center, radius, normal] = feature.get_circle(); // render center const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); @@ -423,7 +427,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); m_circle.model.set_color(colors.back()); m_circle.model.render(); @@ -437,8 +441,8 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const auto& [start, end] = feature.get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); set_matrix_uniforms(feature_matrix); m_cylinder.model.set_color(colors.front()); @@ -505,25 +509,23 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, model_matrix, inv_zoom, true); + render_feature(*m_curr_feature, colors, m_volume_matrix, inv_zoom, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); - render_feature(*m_selected_features.first.feature, colors, - (m_selected_features.first.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); - render_feature(*m_selected_features.second.feature, colors, - (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); } if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -616,6 +618,100 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) +{ + assert(col_1 != nullptr && col_2 != nullptr); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + col_1(); + ImGui::TableSetColumnIndex(1); + col_2(); +}; + +static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) +{ + add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); +}; + +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +}; + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +}; + +#if ENABLE_MEASURE_GIZMO_DEBUG +void GLGizmoMeasure::render_debug_dialog() +{ + auto add_feature_data = [this](const SelectedFeatures::Item& item) { + add_strings_row_to_table(*m_imgui, "Type", ImGuiWrapper::COL_ORANGE_LIGHT, item.source, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + switch (item.feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = item.feature->get_edge(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = item.feature->get_plane(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = item.feature->get_circle(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + std::optional extra_point = item.feature->get_extra_point(); + if (extra_point.has_value()) + add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + }; + + m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) + m_imgui->text("Empty selection"); + else { + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; + if (m_selected_features.first.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Selection 1"); + if (ImGui::BeginTable("Selection 1", 2, flags)) { + add_feature_data(m_selected_features.first); + ImGui::EndTable(); + } + } + if (m_selected_features.second.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Selection 2"); + if (ImGui::BeginTable("Selection 2", 2, flags)) { + add_feature_data(m_selected_features.second); + ImGui::EndTable(); + } + } + } + m_imgui->end(); +} +#endif // ENABLE_MEASURE_GIZMO_DEBUG + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -640,34 +736,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](std::function col_1 = nullptr, std::function col_2 = nullptr) { - assert(col_1 != nullptr && col_2 != nullptr); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - col_1(); - ImGui::TableSetColumnIndex(1); - col_2(); - }; - - auto add_strings_row_to_table = [this, add_row_to_table](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { - add_row_to_table( - [this, &col_1, &col_1_color]() { m_imgui->text_colored(col_1_color, col_1); }, - [this, &col_2, &col_2_color]() { m_imgui->text_colored(col_2_color, col_2); } - ); - }; - - auto format_double = [](double value) { - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); - }; - - auto format_vec3 = [](const Vec3d& v) { - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); - }; - if (ImGui::BeginTable("Commands", 2)) { add_row_to_table( [this]() { @@ -685,10 +753,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ); if (m_selected_features.first.feature.has_value()) - add_strings_row_to_table(CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -696,7 +764,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); if (m_curr_feature.has_value()) { - const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); if (m_mode == EMode::BasicSelection) { if (feature_type != Measure::SurfaceFeatureType::Undef) { @@ -708,49 +775,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - Vec3d position = volume_matrix * m_curr_feature->get_point(); + Vec3d position = m_volume_matrix * m_curr_feature->get_point(); if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: { auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; + from = m_volume_matrix * from; + to = m_volume_matrix * to; if (use_inches) { from = ObjectManipulation::mm_to_in * from; to = ObjectManipulation::mm_to_in * to; } - add_strings_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: { auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + center = m_volume_matrix * center; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) { center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; } - add_strings_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Plane: { auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } } @@ -763,11 +830,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; + Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } } @@ -777,9 +843,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; if (ImGui::BeginTable("Selection", 2, flags)) { - add_strings_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + add_strings_row_to_table(*m_imgui, _u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_strings_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + add_strings_row_to_table(*m_imgui, _u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } @@ -798,28 +864,28 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 2)) { if (measure.angle.has_value()) { - add_strings_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_strings_row_to_table(*m_imgui, _u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { - double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; + double distance = *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { - double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; + double distance = *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { - Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; + Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index d74fb9ed8e..6576787cb5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -33,12 +33,10 @@ class GLGizmoMeasure : public GLGizmoBase { struct Item { - EMode mode{ EMode::BasicSelection }; std::string source; std::optional feature; bool operator == (const Item& other) const { - if (this->mode != other.mode) return false; if (this->source != other.source) return false; return this->feature == other.feature; } @@ -48,7 +46,6 @@ class GLGizmoMeasure : public GLGizmoBase } void reset() { - mode = EMode::BasicSelection; source.clear(); feature.reset(); } @@ -62,12 +59,12 @@ class GLGizmoMeasure : public GLGizmoBase second.reset(); } - bool operator == (const SelectedFeatures& other) const { + bool operator == (const SelectedFeatures & other) const { if (this->first != other.first) return false; return this->second == other.second; } - bool operator != (const SelectedFeatures& other) const { + bool operator != (const SelectedFeatures & other) const { return !operator == (other); } }; @@ -80,6 +77,7 @@ class GLGizmoMeasure : public GLGizmoBase PickingModel m_circle; PickingModel m_plane; + Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; @@ -111,6 +109,10 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); +#if ENABLE_MEASURE_GIZMO_DEBUG + void render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 3d72f224e1ffda8d4aaa90514c508d9e03d20950 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Sep 2022 14:04:18 +0200 Subject: [PATCH 117/327] Measuring: Gizmo measure disabled for sinking volumes --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b434cea9a7..db9fc0dd34 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -229,9 +229,12 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); + if (res) + res &= !selection.get_first_volume()->is_sinking(); + return res; } void GLGizmoMeasure::on_render() From f1a59de7f4c959b0fc6b09f44824ac0c980c5845 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 12:28:16 +0200 Subject: [PATCH 118/327] Measuring: Gizmo measure shows dimensioning for distance point-point --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 146 +++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 8 ++ 2 files changed, 154 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db9fc0dd34..92046c299f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -27,6 +27,9 @@ static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; +static const float TRIANGLE_BASE = 16.0f; +static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; + static const std::string CTRL_STR = #ifdef __APPLE__ "⌘" @@ -537,6 +540,8 @@ void GLGizmoMeasure::on_render() shader->stop_using(); } + + render_dimensioning(); } void GLGizmoMeasure::update_if_needed() @@ -621,6 +626,147 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +void GLGizmoMeasure::render_dimensioning() +{ + if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + auto point_point_2D = [this, shader](const Vec3d& v1, const Vec3d& v2) { + if (v1.isApprox(v2)) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d& projection_matrix = camera.get_projection_matrix(); + const Matrix4d projection_view_matrix = projection_matrix.matrix() * view_matrix.matrix(); + const std::array& viewport = camera.get_viewport(); + const double inv_zoom = camera.get_inv_zoom(); + + auto model_to_world = [this](const Vec3d& model) { + return (Vec3d)(m_volume_matrix * model); + }; + + auto world_to_clip = [&projection_view_matrix](const Vec3d& world) { + return (Vec4d)(projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0)); + }; + + auto clip_to_ndc = [](const Vec4d& clip) { + return Vec2d(clip.x(), clip.y()) / clip.w(); + }; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + + auto ndc_to_ss = [&viewport, half_w, half_h](const Vec2d& ndc) { + return Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h); + }; + + Matrix4d ndc_to_ss_matrix; + ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + const Transform3d ss_to_ndc_matrix = Transform3d(ndc_to_ss_matrix.inverse()); + + // ndc coordinates + const Vec2d v1ndc = clip_to_ndc(world_to_clip(model_to_world(v1))); + const Vec2d v2ndc = clip_to_ndc(world_to_clip(model_to_world(v2))); + const Vec2d v12ndc = v2ndc - v1ndc; + const double v12ndc_len = v12ndc.norm(); + + // screen coordinates + const Vec2d v1ss = ndc_to_ss(v1ndc); + const Vec2d v2ss = ndc_to_ss(v2ndc); + + if (v1ss.isApprox(v2ss)) + return; + + const Vec2d v12ss = v2ss - v1ss; + const double v12ss_len = v12ss.norm(); + + const bool overlap = v12ss_len - 2.0 * TRIANGLE_HEIGHT < 0.0; + + const auto q12ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(v12ss.x(), v12ss.y(), 0.0)); + const auto q21ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(-v12ss.x(), -v12ss.y(), 0.0)); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; + const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + + // stem + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::scale_transform({ v12ss_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + + // arrow 1 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q21ss); + m_dimensioning.triangle.render(); + + // arrow 2 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); + m_dimensioning.triangle.render(); + }; + + shader->start_using(); + + if (!m_dimensioning.line.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(1.0f, 0.0f, 0.0f)); + + // indices + init_data.add_line(0, 1); + + m_dimensioning.line.init_from(std::move(init_data)); + } + + if (!m_dimensioning.triangle.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(3); + init_data.reserve_indices(3); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, 0.5f * TRIANGLE_BASE, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, -0.5f * TRIANGLE_BASE, 0.0f)); + + // indices + init_data.add_triangle(0, 1, 2); + + m_dimensioning.triangle.init_from(std::move(init_data)); + } + + glsafe(::glDisable(GL_DEPTH_TEST)); + + if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_point_2D(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + } + + glsafe(::glEnable(GL_DEPTH_TEST)); + + shader->stop_using(); +} + static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) { assert(col_1 != nullptr && col_2 != nullptr); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 6576787cb5..b6bd1ee5af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -76,6 +76,12 @@ class GLGizmoMeasure : public GLGizmoBase PickingModel m_cylinder; PickingModel m_circle; PickingModel m_plane; + struct Dimensioning + { + GLModel line; + GLModel triangle; + }; + Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; @@ -109,6 +115,8 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); + void render_dimensioning(); + #if ENABLE_MEASURE_GIZMO_DEBUG void render_debug_dialog(); #endif // ENABLE_MEASURE_GIZMO_DEBUG From 00bcddb19d074c023a7478511a245ffb716f222e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 14:42:04 +0200 Subject: [PATCH 119/327] Measuring: Gizmo measure shows dimensioning for distance point-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 166 +++++++++++++++++------ 1 file changed, 125 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 92046c299f..4caae10f43 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -626,6 +626,79 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +class DimensioningHelper +{ + struct Cache + { + std::array viewport; + Matrix4d ndc_to_ss_matrix; + Transform3d ndc_to_ss_matrix_inverse; + }; + + static Cache s_cache; + +public: + static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { + return world_matrix * model; + } + + 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 Vec3d clip_to_ndc(const Vec4d& clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + } + + 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 Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); + } + + static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); + } + + static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); + } + + static const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix; + } + + static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix_inverse; + } + +private: + static void update(const std::array& viewport) { + if (s_cache.viewport == viewport) + return; + + std::cout << "DimensioningHelper::update()\n"; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); + s_cache.viewport = viewport; + } +}; + +DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; + void GLGizmoMeasure::render_dimensioning() { if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) @@ -635,53 +708,17 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point_2D = [this, shader](const Vec3d& v1, const Vec3d& v2) { + auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2) { if (v1.isApprox(v2)) return; const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - const Transform3d& projection_matrix = camera.get_projection_matrix(); - const Matrix4d projection_view_matrix = projection_matrix.matrix() * view_matrix.matrix(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); - const double inv_zoom = camera.get_inv_zoom(); - - auto model_to_world = [this](const Vec3d& model) { - return (Vec3d)(m_volume_matrix * model); - }; - - auto world_to_clip = [&projection_view_matrix](const Vec3d& world) { - return (Vec4d)(projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0)); - }; - - auto clip_to_ndc = [](const Vec4d& clip) { - return Vec2d(clip.x(), clip.y()) / clip.w(); - }; - - const double half_w = 0.5 * double(viewport[2]); - const double half_h = 0.5 * double(viewport[3]); - - auto ndc_to_ss = [&viewport, half_w, half_h](const Vec2d& ndc) { - return Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h); - }; - - Matrix4d ndc_to_ss_matrix; - ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, - 0.0, half_h, 0.0, double(viewport[1]) + half_h, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0; - - const Transform3d ss_to_ndc_matrix = Transform3d(ndc_to_ss_matrix.inverse()); - - // ndc coordinates - const Vec2d v1ndc = clip_to_ndc(world_to_clip(model_to_world(v1))); - const Vec2d v2ndc = clip_to_ndc(world_to_clip(model_to_world(v2))); - const Vec2d v12ndc = v2ndc - v1ndc; - const double v12ndc_len = v12ndc.norm(); // screen coordinates - const Vec2d v1ss = ndc_to_ss(v1ndc); - const Vec2d v2ss = ndc_to_ss(v2ndc); + const Vec2d v1ss = DimensioningHelper::model_to_ss(v1, m_volume_matrix, projection_view_matrix, viewport); + const Vec2d v2ss = DimensioningHelper::model_to_ss(v2, m_volume_matrix, projection_view_matrix, viewport); if (v1ss.isApprox(v2ss)) return; @@ -699,6 +736,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + // stem shader->set_uniform("view_model_matrix", overlap ? ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : @@ -718,6 +757,43 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; + auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { + const Vec3d e1v = v - e.first; + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1e2_unit = e1e2.normalized(); + const Vec3d v_proj_on_e1e2 = e.first + e1v.dot(e1e2_unit) * e1e2_unit; + point_point(v, v_proj_on_e1e2); + + const Vec3d v_proj_on_e1e2e1 = v_proj_on_e1e2 - e.first; + bool on_e1_side = v_proj_on_e1e2e1.dot(e1e2) < 0.0; + bool on_e2_side = v_proj_on_e1e2e1.norm() > e1e2.norm(); + if (on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec2d v_proj_on_e1e2ss = DimensioningHelper::model_to_ss(v_proj_on_e1e2, m_volume_matrix, projection_view_matrix, viewport); + auto render_extension = [this, &v_proj_on_e1e2ss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); + if (!pss.isApprox(v_proj_on_e1e2ss)) { + const Vec2d pv_proj_on_e1e2ss = v_proj_on_e1e2ss - pss; + const double pv_proj_on_e1e2ss_len = pv_proj_on_e1e2ss.norm(); + + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_proj_on_e1e2ss.x(), pv_proj_on_e1e2ss.y(), 0.0)); + + shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * + Geometry::scale_transform({ pv_proj_on_e1e2ss_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } + }; + + render_extension(on_e1_side ? e.first : e.second); + } + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -759,7 +835,15 @@ void GLGizmoMeasure::render_dimensioning() if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point_2D(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } glsafe(::glEnable(GL_DEPTH_TEST)); From bf0f7c609dd3f6c9d35fde8a4f15ef551ad8d93f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 15:27:49 +0200 Subject: [PATCH 120/327] Measuring: Gizmo measure shows dimensioning for distance point-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 45 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4caae10f43..c1e6a0c9ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -758,34 +758,32 @@ void GLGizmoMeasure::render_dimensioning() }; auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { - const Vec3d e1v = v - e.first; const Vec3d e1e2 = e.second - e.first; const Vec3d e1e2_unit = e1e2.normalized(); - const Vec3d v_proj_on_e1e2 = e.first + e1v.dot(e1e2_unit) * e1e2_unit; - point_point(v, v_proj_on_e1e2); + const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; + point_point(v, v_proj); - const Vec3d v_proj_on_e1e2e1 = v_proj_on_e1e2 - e.first; - bool on_e1_side = v_proj_on_e1e2e1.dot(e1e2) < 0.0; - bool on_e2_side = v_proj_on_e1e2e1.norm() > e1e2.norm(); + const Vec3d v_proje1 = v_proj - e.first; + bool on_e1_side = v_proje1.dot(e1e2) < 0.0; + bool on_e2_side = v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); - shader->set_uniform("projection_matrix", Transform3d::Identity()); - - const Vec2d v_proj_on_e1e2ss = DimensioningHelper::model_to_ss(v_proj_on_e1e2, m_volume_matrix, projection_view_matrix, viewport); - auto render_extension = [this, &v_proj_on_e1e2ss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d v_projss = DimensioningHelper::model_to_ss(v_proj, m_volume_matrix, projection_view_matrix, viewport); + auto render_extension = [this, &v_projss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); - if (!pss.isApprox(v_proj_on_e1e2ss)) { - const Vec2d pv_proj_on_e1e2ss = v_proj_on_e1e2ss - pss; - const double pv_proj_on_e1e2ss_len = pv_proj_on_e1e2ss.norm(); + if (!pss.isApprox(v_projss)) { + const Vec2d pv_projss = v_projss - pss; + const double pv_projss_len = pv_projss.norm(); - const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_proj_on_e1e2ss.x(), pv_proj_on_e1e2ss.y(), 0.0)); + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_projss.x(), pv_projss.y(), 0.0)); + shader->set_uniform("projection_matrix", Transform3d::Identity()); shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * - Geometry::scale_transform({ pv_proj_on_e1e2ss_len, 1.0f, 1.0f })); + Geometry::scale_transform({ pv_projss_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; @@ -794,6 +792,15 @@ void GLGizmoMeasure::render_dimensioning() } }; + auto point_plane = [this, shader, point_point](const Vec3d& v, const std::tuple& p) { + const auto& [idx, normal, origin] = p; + const double distance = normal.dot(v - origin); + if (std::abs(distance) < EPSILON) + return; + + point_point(v, v - distance * normal); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -845,6 +852,14 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 8d98f0869dc6c6c6e3c67da4bb0670f17c7acf36 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 08:30:19 +0200 Subject: [PATCH 121/327] Measuring: Gizmo measure shows dimensioning for distance edge-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 39 +++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c1e6a0c9ff..d166982144 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -792,7 +792,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [this, shader, point_point](const Vec3d& v, const std::tuple& p) { + auto point_plane = [shader, point_point](const Vec3d& v, const std::tuple& p) { const auto& [idx, normal, origin] = p; const double distance = normal.dot(v - origin); if (std::abs(distance) < EPSILON) @@ -801,6 +801,39 @@ void GLGizmoMeasure::render_dimensioning() point_point(v, v - distance * normal); }; + auto edge_edge = [this, shader, point_point](const std::pair& e1, const std::pair& e2) { + auto min_distance_edge_edge = [](const std::pair& e1, const std::pair& e2) { + auto distance_point_edge = [](const Vec3d& v, const std::pair& e) { + const Vec3d e1v = v - e.first; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; + return std::make_pair((e1v - v_proj).norm(), v_proj); + }; + + std::vector> distances; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); + distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); + const auto dist_e11e2 = distance_point_edge(e1.first, e2); + distances.emplace_back(std::make_tuple(dist_e11e2.first, e1.first, dist_e11e2.second)); + const auto dist_e12e2 = distance_point_edge(e1.second, e2); + distances.emplace_back(std::make_tuple(dist_e12e2.first, e1.second, dist_e12e2.second)); + const auto dist_e21e1 = distance_point_edge(e2.first, e1); + distances.emplace_back(std::make_tuple(dist_e21e1.first, e2.first, dist_e21e1.second)); + const auto dist_e22e1 = distance_point_edge(e2.second, e1); + distances.emplace_back(std::make_tuple(dist_e22e1.first, e2.second, dist_e22e1.second)); + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); + }; + + const auto [dist, v1, v2] = min_distance_edge_edge(e1, e2); + point_point(v1, v2); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -860,6 +893,10 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 3eae55bd0669c82cf3d1bff10d4f23455f3a5d7a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 09:57:07 +0200 Subject: [PATCH 122/327] Measuring: Gizmo measure shows dimensioning for distance point-circle --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 122 ++++++++++++++--------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d166982144..bc89051520 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -19,6 +19,10 @@ namespace Slic3r { namespace GUI { +using Edge = std::pair; +using Plane = std::tuple; +using Circle = std::tuple; + static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; @@ -64,6 +68,46 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } +static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) +{ + const double distance = std::get<1>(p).dot(v - std::get<2>(p)); + return std::make_tuple(std::abs(distance), v, v - distance * std::get<1>(p)); +} + +static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) +{ + const Vec3d e1v = v - e.first; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d v_proj = e.first + e1e2_unit.dot(e1v) * e1e2_unit; + return std::make_tuple((e1v - v_proj).norm(), v, v_proj); +} + +static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) +{ + const auto& [center, radius, normal] = c; + const auto [distance, v1, v2] = distance_point_plane(v, std::make_tuple(0, normal, center)); + const Vec3d p_on_circle = center + radius * (v2 - center).normalized(); + return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); +} + +static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) +{ + std::vector> distances; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); + distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); + distances.emplace_back(distance_point_edge(e1.first, e2)); + distances.emplace_back(distance_point_edge(e1.second, e2)); + distances.emplace_back(distance_point_edge(e2.first, e1)); + distances.emplace_back(distance_point_edge(e2.second, e1)); + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -757,12 +801,11 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1e2_unit = e1e2.normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; - point_point(v, v_proj); + auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { + const auto [distance, v1, v_proj] = distance_point_edge(v, e); + point_point(v1, v_proj); + const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; bool on_e1_side = v_proje1.dot(e1e2) < 0.0; bool on_e2_side = v_proje1.norm() > e1e2.norm(); @@ -792,45 +835,18 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [shader, point_point](const Vec3d& v, const std::tuple& p) { - const auto& [idx, normal, origin] = p; - const double distance = normal.dot(v - origin); - if (std::abs(distance) < EPSILON) - return; - - point_point(v, v - distance * normal); + auto point_plane = [point_point](const Vec3d& v, const Plane& p) { + const auto [distance, v1, v2] = distance_point_plane(v, p); + point_point(v1, v2); }; - auto edge_edge = [this, shader, point_point](const std::pair& e1, const std::pair& e2) { - auto min_distance_edge_edge = [](const std::pair& e1, const std::pair& e2) { - auto distance_point_edge = [](const Vec3d& v, const std::pair& e) { - const Vec3d e1v = v - e.first; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; - return std::make_pair((e1v - v_proj).norm(), v_proj); - }; + auto point_circle = [point_point](const Vec3d& v, const Circle& c) { + const auto [distance, v1, v2] = distance_point_circle(v, c); + point_point(v1, v2); + }; - std::vector> distances; - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - const auto dist_e11e2 = distance_point_edge(e1.first, e2); - distances.emplace_back(std::make_tuple(dist_e11e2.first, e1.first, dist_e11e2.second)); - const auto dist_e12e2 = distance_point_edge(e1.second, e2); - distances.emplace_back(std::make_tuple(dist_e12e2.first, e1.second, dist_e12e2.second)); - const auto dist_e21e1 = distance_point_edge(e2.first, e1); - distances.emplace_back(std::make_tuple(dist_e21e1.first, e2.first, dist_e21e1.second)); - const auto dist_e22e1 = distance_point_edge(e2.second, e1); - distances.emplace_back(std::make_tuple(dist_e22e1.first, e2.second, dist_e22e1.second)); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); - }; - - const auto [dist, v1, v2] = min_distance_edge_edge(e1, e2); + auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { + const auto [distance, v1, v2] = min_distance_edge_edge(e1, e2); point_point(v1, v2); }; @@ -873,26 +889,42 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); + // point-point if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); } + // point-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); } - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } + // point-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); } + // point-circle + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); + } + // edge-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); + } + // plane-point else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); } + // circle-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); + } + // edge-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); From e5c22b5694eab16d50a5220effef0ca704b6f3ff Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 11:24:44 +0200 Subject: [PATCH 123/327] Measuring: Use eigen library in distance calculations for Gizmo measure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bc89051520..bfcef7365b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -70,23 +70,22 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) { - const double distance = std::get<1>(p).dot(v - std::get<2>(p)); - return std::make_tuple(std::abs(distance), v, v - distance * std::get<1>(p)); + const auto& [idx, normal, origin] = p; + const Eigen::Hyperplane plane(normal, origin); + return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); } static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) { - const Vec3d e1v = v - e.first; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(e1v) * e1e2_unit; - return std::make_tuple((e1v - v_proj).norm(), v, v_proj); + const Eigen::ParametrizedLine line(e.first, (e.second - e.first).normalized()); + return std::make_tuple(line.distance(v), v, line.projection(v)); } static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) { const auto& [center, radius, normal] = c; - const auto [distance, v1, v2] = distance_point_plane(v, std::make_tuple(0, normal, center)); - const Vec3d p_on_circle = center + radius * (v2 - center).normalized(); + const Eigen::Hyperplane plane(normal, center); + const Vec3d p_on_circle = center + radius * (plane.projection(v) - center).normalized(); return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } From 3a2a6d4a2f47f5ecfd7bbe13d97309b6d65842dc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 13:28:15 +0200 Subject: [PATCH 124/327] Measuring: Gizmo measure shows dimensioning for distance plane-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bfcef7365b..9e0d2f60f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,6 +89,14 @@ static std::tuple distance_point_circle(const Vec3d& v, co return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } +static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : + std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); +} + static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; @@ -726,8 +734,6 @@ private: if (s_cache.viewport == viewport) return; - std::cout << "DimensioningHelper::update()\n"; - const double half_w = 0.5 * double(viewport[2]); const double half_h = 0.5 * double(viewport[3]); s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, @@ -849,6 +855,11 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; + auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { + const auto [distance, v1, v2] = distance_plane_plane(p1, p2); + point_point(v1, v2); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -928,6 +939,11 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // plane-plane + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 81d28c545cc1bbb31ee618934ae3afc72f3fc668 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 10:01:02 +0200 Subject: [PATCH 125/327] Measuring: Gizmo measure shows dimensioning for distance edge-circle --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 62 +++++++++++++++++++----- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9e0d2f60f8..bdb0420a8c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,15 +89,7 @@ static std::tuple distance_point_circle(const Vec3d& v, co return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} - -static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) +static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); @@ -115,6 +107,37 @@ static std::tuple min_distance_edge_edge(const Edge& e1, c return distances.front(); } +static std::tuple distance_edge_circle(const Edge& e, const Circle& c) +{ + const auto& [center, radius, normal] = c; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + double dot = std::abs(e1e2_unit.dot(normal)); + + if (dot < EPSILON) { + // edge parallel to circle's plane + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Vec3d inter = line.intersectionPoint(plane); + return distance_point_circle(inter, c); + } + else if (std::abs(dot - 1.0) < EPSILON) + // edge parallel to circle's normal + return distance_point_circle(e.first, c); + else { + const auto [distance1, v11, v12] = distance_point_circle(e.first, c); + const auto [distance2, v21, v22] = distance_point_circle(e.second, c); + return (distance1 <= distance2) ? std::make_tuple(distance1, v11, v12) : std::make_tuple(distance2, v21, v22); + } +} + +static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : + std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -812,8 +835,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; - bool on_e1_side = v_proje1.dot(e1e2) < 0.0; - bool on_e2_side = v_proje1.norm() > e1e2.norm(); + const bool on_e1_side = v_proje1.dot(e1e2) < 0.0; + const bool on_e2_side = v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); @@ -851,7 +874,12 @@ void GLGizmoMeasure::render_dimensioning() }; auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { - const auto [distance, v1, v2] = min_distance_edge_edge(e1, e2); + const auto [distance, v1, v2] = distance_edge_edge(e1, e2); + point_point(v1, v2); + }; + + auto edge_circle = [point_point](const Edge& e, const Circle& c) { + const auto [distance, v1, v2] = distance_edge_circle(e, c); point_point(v1, v2); }; @@ -939,11 +967,21 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // edge-circle + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); + } // plane-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } + // circle-edge + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From a30a2547243c28dccb88de8776c8d3f26f427245 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 19 Sep 2022 11:14:29 +0200 Subject: [PATCH 126/327] 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 9622da1457..e9e5137bec 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 127/327] 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 7b6c729f8d..905e39db38 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 81a69e4256..a7892a9cd2 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 97f3cb9a88..a08836c28b 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 9254080089..7c41a64b9d 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 bbf1f7652c..74fe82494b 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 5df4236a6a..726f228caa 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 128/327] 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 905e39db38..d235ad4c5d 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 129/327] 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 74fe82494b..e7c1d0fe9a 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 c87c9886a51cdf2a670aff4dd953e1d9e37aa72e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 11:44:03 +0200 Subject: [PATCH 130/327] Follow-up of 81d28c545cc1bbb31ee618934ae3afc72f3fc668 - Distance edge-circle calculated as in Fusion 360 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bdb0420a8c..89396071c7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -110,24 +110,25 @@ static std::tuple distance_edge_edge(const Edge& e1, const static std::tuple distance_edge_circle(const Edge& e, const Circle& c) { const auto& [center, radius, normal] = c; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - double dot = std::abs(e1e2_unit.dot(normal)); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = e1e2.normalized(); - if (dot < EPSILON) { - // edge parallel to circle's plane - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line(e.first, e1e2_unit); - const Vec3d inter = line.intersectionPoint(plane); - return distance_point_circle(inter, c); - } - else if (std::abs(dot - 1.0) < EPSILON) - // edge parallel to circle's normal - return distance_point_circle(e.first, c); - else { - const auto [distance1, v11, v12] = distance_point_circle(e.first, c); - const auto [distance2, v21, v22] = distance_point_circle(e.second, c); - return (distance1 <= distance2) ? std::make_tuple(distance1, v11, v12) : std::make_tuple(distance2, v21, v22); - } + std::vector> distances; + distances.emplace_back(distance_point_circle(e.first, c)); + distances.emplace_back(distance_point_circle(e.second, c)); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(distance_point_circle(inter, c)); + + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); } static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) From 116644677436b51b935d4250081a42544c1b2cb4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 13:22:09 +0200 Subject: [PATCH 131/327] Follow-up of 8d98f0869dc6c6c6e3c67da4bb0670f17c7acf36 - Distance edge-edge calculated as in Fusion 360 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 89396071c7..5a477034ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -92,14 +92,22 @@ static std::tuple distance_point_circle(const Vec3d& v, co static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; + auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { + const auto [distance, v1, v2] = distance_point_edge(v, e); + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(std::make_tuple(distance, v, v2)); + }; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - distances.emplace_back(distance_point_edge(e1.first, e2)); - distances.emplace_back(distance_point_edge(e1.second, e2)); - distances.emplace_back(distance_point_edge(e2.first, e1)); - distances.emplace_back(distance_point_edge(e2.second, e1)); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); std::sort(distances.begin(), distances.end(), [](const std::tuple& item1, const std::tuple& item2) { return std::get<0>(item1) < std::get<0>(item2); From e93ff4d087ff048c32f5d09e85810b9e15bed9a0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 19 Sep 2022 17:53:01 +0200 Subject: [PATCH 132/327] 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 d235ad4c5d..9e1183c07b 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 a7892a9cd2..11c7b1abdc 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 133/327] 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 9e1183c07b..2adebbbece 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 11c7b1abdc..ac3113a0a4 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 024b238196..3946e54e13 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 134/327] 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 7e22127cd4..4de88101c1 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 3e42c86707..d13056f952 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 e7c1d0fe9a..b6f95eead5 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 a79e78cee106afe64bfca206f7719c10e9893e26 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 21 Sep 2022 09:16:49 +0200 Subject: [PATCH 135/327] Measuring: Gizmo measure shows dimensioning for angle edge-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 135 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 +- 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5a477034ea..f6a6a46ca5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -31,7 +31,7 @@ static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; -static const float TRIANGLE_BASE = 16.0f; +static const float TRIANGLE_BASE = 10.0f; static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; static const std::string CTRL_STR = @@ -782,6 +782,8 @@ DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix void GLGizmoMeasure::render_dimensioning() { + static SelectedFeatures last_selected_features; + if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) return; @@ -844,8 +846,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; - const bool on_e1_side = v_proje1.dot(e1e2) < 0.0; - const bool on_e2_side = v_proje1.norm() > e1e2.norm(); + const bool on_e1_side = v_proje1.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); @@ -882,9 +884,131 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; - auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { + auto arc_edge_edge = [this, shader](Edge e1, Edge e2) { + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + // edges are parallel, return + return; + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // center in world coordinate + const Vec3d center = qp.inverse() * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // revert edges, if needed (we want them to move away from the center) + unsigned int revert_count = 0; + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e1.first, e1.second); + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + ++revert_count; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e2.first, e2.second); + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + ++revert_count; + } + + if (revert_count == 1) { + normal = -normal; + qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + qp_inverse = qp.inverse(); + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + if (!m_dimensioning.arc.is_initialized()) { + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(resolution + 1); + init_data.reserve_indices(resolution + 1); + + // vertices + indices + const double step = angle / double(resolution); + for (unsigned int i = 0; i <= resolution; ++i) { + const double a = step * double(i); + const Vec3d v = radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + init_data.add_vertex((Vec3f)v.cast()); + init_data.add_index(i); + } + + m_dimensioning.arc.init_from(std::move(init_data)); + } + + auto render_edge_entension = [this, shader, ¢er, radius](const Edge& e, bool coplanar) { + const Vec3d e1center = center - e.first; + const Vec3d e1e2 = e.second - e.first; + const bool on_e1_side = e1center.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && e1center.norm() > e1e2.norm(); + if (!coplanar || on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + auto render_extension = [this, shader, &camera, ¢er, radius, coplanar](const Vec3d& p) { + const Vec3d centerp = p - center; + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), centerp.normalized()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * q * + Geometry::scale_transform({ coplanar ? centerp.norm() : radius, 1.0f, 1.0f })); + m_dimensioning.line.render(); + }; + render_extension(on_e1_side ? e.first : e.second); + } + }; + + auto render_arc = [this, shader, ¢er]() { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); + }; + + render_edge_entension({ e11_proj , e12_proj }, true); + render_edge_entension({ e21_proj , e22_proj }, coplanar); + render_arc(); + }; + + auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { + // distance const auto [distance, v1, v2] = distance_edge_edge(e1, e2); point_point(v1, v2); + // arc + arc_edge_edge(e1, e2); }; auto edge_circle = [point_point](const Edge& e, const Circle& c) { @@ -934,6 +1058,9 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.init_from(std::move(init_data)); } + if (last_selected_features != m_selected_features) + m_dimensioning.arc.reset(); + glsafe(::glDisable(GL_DEPTH_TEST)); // point-point diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index b6bd1ee5af..11e861cc7c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -80,7 +80,8 @@ class GLGizmoMeasure : public GLGizmoBase { GLModel line; GLModel triangle; - }; + GLModel arc; + }; Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; From e676d40df5c90ea2773f12b6aed312c628612887 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 21 Sep 2022 13:40:24 +0200 Subject: [PATCH 136/327] 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 f9fdf575b9..678c8fe20c 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 2adebbbece..00a11ff37d 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 ac3113a0a4..5bc47f49c5 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 e9e5137bec..22451c60ca 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 137/327] 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 00a11ff37d..4986688968 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 5bc47f49c5..fc2347c643 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 ba21f54afb245333dc1adeea1d03fa128af9fb54 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 23 Sep 2022 12:44:06 +0200 Subject: [PATCH 138/327] Measuring: Gizmo measure shows dimensioning for angle edge-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 284 +++++++++++++++-------- 1 file changed, 181 insertions(+), 103 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f6a6a46ca5..fc49e9883f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -75,20 +75,33 @@ static std::tuple distance_point_plane(const Vec3d& v, con return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); } +static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) +{ + return (to - from).normalized(); +} + +static Vec3d edge_direction(const Edge& e) +{ + return vector_direction(e.first, e.second); +} + +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) { - const Eigen::ParametrizedLine line(e.first, (e.second - e.first).normalized()); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); return std::make_tuple(line.distance(v), v, line.projection(v)); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) { const auto& [center, radius, normal] = c; const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * (plane.projection(v) - center).normalized(); + const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; @@ -115,18 +128,19 @@ static std::tuple distance_edge_edge(const Edge& e1, const return distances.front(); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_circle(const Edge& e, const Circle& c) { const auto& [center, radius, normal] = c; const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = e1e2.normalized(); + const Vec3d e1e2_unit = vector_direction(e.first, e.second); std::vector> distances; distances.emplace_back(distance_point_circle(e.first, c)); distances.emplace_back(distance_point_circle(e.second, c)); const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); const Vec3d inter = line.intersectionPoint(plane); const Vec3d e1inter = inter - e.first; if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) @@ -139,6 +153,7 @@ static std::tuple distance_edge_circle(const Edge& e, cons return distances.front(); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) { const auto& [idx1, normal1, origin1] = p1; @@ -147,6 +162,71 @@ static std::tuple distance_plane_plane(const Plane& p1, co std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); } +// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar +// After return, the edges are oriented so that they point away from their intersection point +static std::tuple angle_edge_edge(Edge& e1, Edge& e2) +{ + Vec3d e1_unit = edge_direction(e1); + Vec3d e2_unit = edge_direction(e2); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return std::make_tuple(0.0, e1.first, 0.0, true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e1.first, e1.second); + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e2.first, e2.second); + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return std::make_tuple(angle, center, radius, coplanar); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -884,73 +964,21 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; - auto arc_edge_edge = [this, shader](Edge e1, Edge e2) { - Vec3d e1_unit = (e1.second - e1.first).normalized(); - Vec3d e2_unit = (e2.second - e2.first).normalized(); - const double dot = e1_unit.dot(e2_unit); - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - // edges are parallel, return + auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { + Edge e1copy = e1; + Edge e2copy = e2; + const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + + if (radius == 0.0) return; - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); + assert(force_radius == nullptr || *force_radius > 0.0); - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // center in world coordinate - const Vec3d center = qp.inverse() * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // revert edges, if needed (we want them to move away from the center) - unsigned int revert_count = 0; - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - ++revert_count; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - ++revert_count; - } - - if (revert_count == 1) { - normal = -normal; - qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - qp_inverse = qp.inverse(); - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + const Vec3d e1_unit = edge_direction(e1copy); + const Vec3d e2_unit = edge_direction(e2copy); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); @@ -964,7 +992,7 @@ void GLGizmoMeasure::render_dimensioning() const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); - const Vec3d v = radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); init_data.add_vertex((Vec3f)v.cast()); init_data.add_index(i); } @@ -972,35 +1000,70 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.init_from(std::move(init_data)); } - auto render_edge_entension = [this, shader, ¢er, radius](const Edge& e, bool coplanar) { - const Vec3d e1center = center - e.first; - const Vec3d e1e2 = e.second - e.first; - const bool on_e1_side = e1center.dot(e1e2) < -EPSILON; - const bool on_e2_side = !on_e1_side && e1center.norm() > e1e2.norm(); - if (!coplanar || on_e1_side || on_e2_side) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - auto render_extension = [this, shader, &camera, ¢er, radius, coplanar](const Vec3d& p) { - const Vec3d centerp = p - center; - const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), centerp.normalized()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * q * - Geometry::scale_transform({ coplanar ? centerp.norm() : radius, 1.0f, 1.0f })); - m_dimensioning.line.render(); - }; - render_extension(on_e1_side ? e.first : e.second); - } - }; + // render arc + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); - auto render_arc = [this, shader, ¢er]() { + // render edge 1 extension + const Vec3d e11e12 = e1copy.second - e1copy.first; + const Vec3d e11center = center - e1copy.first; + const double e11center_len = e11center.norm(); + if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); - m_dimensioning.arc.render(); - }; + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } - render_edge_entension({ e11_proj , e12_proj }, true); - render_edge_entension({ e21_proj , e22_proj }, coplanar); - render_arc(); + // render edge 2 extension + const Vec3d e21center = center - e2copy.first; + const double e21center_len = e21center.norm(); + if (e21center_len > EPSILON) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } + }; + + auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + const auto& [idx, normal, origin] = p; + const Vec3d e1e2 = e.second - e.first; + const double abs_dot = std::abs(normal.dot(edge_direction(e))); + if (abs_dot < EPSILON || std::abs(abs_dot - 1.0) < EPSILON) + return; + + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // ensure the edge is pointing away from the intersection + Edge ecopy = e; + Vec3d e1e2copy = e1e2; + if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { + std::swap(ecopy.first, ecopy.second); + e1e2copy = -e1e2copy; + } + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2copy); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2copy.norm() * edge_on_plane_unit }; + + const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); + const double radius = (inters - e1e2copy_mid).norm(); + arc_edge_edge(ecopy, edge_on_plane, &radius); }; auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { @@ -1011,6 +1074,11 @@ void GLGizmoMeasure::render_dimensioning() arc_edge_edge(e1, e2); }; + auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { + // arc + arc_edge_plane(e, p); + }; + auto edge_circle = [point_point](const Edge& e, const Circle& c) { const auto [distance, v1, v2] = distance_edge_circle(e, c); point_point(v1, v2); @@ -1088,31 +1156,41 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } // edge-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // edge-plane + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); + } // edge-circle else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); } + // plane-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); + } + // plane-edge + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); + } // plane-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } + // circle-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); + } // circle-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { From 4dd005a554439e3fd87e4a34f83f4ec821fe7e5d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 26 Sep 2022 13:17:41 +0200 Subject: [PATCH 139/327] Fixed bug in get_measurement() function --- src/libslic3r/Measure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 1a79369a3e..c2b7edd5e2 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -518,7 +518,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto& [idx2, normal2, pt2] = f2.get_plane(); double angle = 0.; - if (! normal1.isApprox(normal2)) { + if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); result.distance_infinite = plane.absDistance(pt2); From 66e2c3b30a6208ea373fc6878c13c71f6363c7ad Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 26 Sep 2022 17:23:40 +0200 Subject: [PATCH 140/327] 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 82b5fad57d..3d245a7b12 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 21b0523370..6981946fbc 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 4986688968..1260ee3bbd 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 fc2347c643..2df337b043 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 2e74270da9..16433bc79e 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 7f351e0b9a..4bf441aa15 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 7e35e935f0..9ee1af272d 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 141/327] 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 ecb1572185..5000285255 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 3d245a7b12..d4c81299fc 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 6981946fbc..16142ec05d 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 4bf441aa15..d09de9cf30 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 9ee1af272d..1d75762666 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 b71d61f0f7b895e7f63c117b2cffd90712cbbf1d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 27 Sep 2022 10:21:57 +0200 Subject: [PATCH 142/327] Measuring - Refactoring in GLGizmoMeasure related to scene raycasters state cache --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 25 +++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 9 +++++++-- src/slic3r/GUI/SceneRaycaster.cpp | 25 ++++++++++++++++++++++++ src/slic3r/GUI/SceneRaycaster.hpp | 4 ++++ 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b5fea57bea..ed82a3a8da 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5517,11 +5517,11 @@ void GLCanvas3D::_picking_pass() ImGui::Separator(); imgui.text("Registered for picking:"); - sprintf(buf, "Beds: %d", (int)m_scene_raycaster.beds_count()); + sprintf(buf, "Beds: %d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); imgui.text(std::string(buf)); - sprintf(buf, "Volumes: %d", (int)m_scene_raycaster.volumes_count()); + sprintf(buf, "Volumes: %d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); imgui.text(std::string(buf)); - sprintf(buf, "Gizmo elements: %d", (int)m_scene_raycaster.gizmos_count()); + sprintf(buf, "Gizmo elements: %d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); imgui.text(std::string(buf)); imgui.end(); #endif // ENABLE_RAYCAST_PICKING_DEBUG diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index fc49e9883f..c511f7e501 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -372,11 +372,13 @@ void GLGizmoMeasure::on_set_state() else { m_mode = EMode::BasicSelection; // store current state of scene raycaster for later use - m_scene_raycaster_state.clear(); - m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (m_scene_raycasters != nullptr) { - for (const auto& r : *m_scene_raycasters) { - m_scene_raycaster_state.emplace_back(r->is_active()); + m_scene_raycasters.clear(); + auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (scene_raycasters != nullptr) { + m_scene_raycasters.reserve(scene_raycasters->size()); + for (auto r : *scene_raycasters) { + SceneRaycasterState state = { r, r->is_active() }; + m_scene_raycasters.emplace_back(state); } } } @@ -772,20 +774,15 @@ void GLGizmoMeasure::update_if_needed() void GLGizmoMeasure::disable_scene_raycasters() { - if (m_scene_raycasters != nullptr) { - for (auto r : *m_scene_raycasters) { - r->set_active(false); - } + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(false); } } void GLGizmoMeasure::restore_scene_raycasters_state() { - if (m_scene_raycasters != nullptr) { - assert(m_scene_raycasters->size() == m_scene_raycaster_state.size()); - for (size_t i = 0; i < m_scene_raycasters->size(); ++i) { - (*m_scene_raycasters)[i]->set_active(m_scene_raycaster_state[i]); - } + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(r.state); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 11e861cc7c..0e967472ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -89,8 +89,13 @@ class GLGizmoMeasure : public GLGizmoBase std::map> m_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; - std::vector>* m_scene_raycasters{ nullptr }; - std::vector m_scene_raycaster_state; + struct SceneRaycasterState + { + std::shared_ptr raycaster{ nullptr }; + bool state{true}; + + }; + std::vector m_scene_raycasters; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 49a59789e3..78cb59c1bc 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -173,6 +173,31 @@ void SceneRaycaster::render_hit(const Camera& camera) shader->stop_using(); } + +size_t SceneRaycaster::active_beds_count() const { + size_t count = 0; + for (const auto& b : m_bed) { + if (b->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_volumes_count() const { + size_t count = 0; + for (const auto& v : m_volumes) { + if (v->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_gizmos_count() const { + size_t count = 0; + for (const auto& g : m_gizmos) { + if (g->is_active()) + ++count; + } + return count; +} #endif // ENABLE_RAYCAST_PICKING_DEBUG std::vector>* SceneRaycaster::get_raycasters(EType type) diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 8993c51a98..7acf82fe11 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -100,6 +100,10 @@ public: size_t beds_count() const { return m_bed.size(); } size_t volumes_count() const { return m_volumes.size(); } size_t gizmos_count() const { return m_gizmos.size(); } + + size_t active_beds_count() const; + size_t active_volumes_count() const; + size_t active_gizmos_count() const; #endif // ENABLE_RAYCAST_PICKING_DEBUG private: From 0201a5055aaa1a52e95a21c852b71200f5fc9145 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 27 Sep 2022 13:54:44 +0200 Subject: [PATCH 143/327] =?UTF-8?q?Cut=20WIP:=20*=20Suppress=20to=20split?= =?UTF-8?q?=20cut=20objects=20*=20ObjectList:=20=20=20*=20Use=20another=20?= =?UTF-8?q?icons=20to=20mark=20the=20cut=20objects=20and=20connectors?= =?UTF-8?q?=C2=A0=20=20=20*=20For=20the=20cut=20object=20show=20parts,=20w?= =?UTF-8?q?hich=20are=20not=20connectors=20*=20Set=20different=20colors=20?= =?UTF-8?q?for=20the=20Plugs=20and=20Dowels=20*=20CutGizmo:=20=20=20*=20In?= =?UTF-8?q?validate=20CutGizmo=20after=20changes=20in=20ObjectList=20or=20?= =?UTF-8?q?perform=20a=20cut=20=20=20*=20CupPlane=20in=20Connectors=20mode?= =?UTF-8?q?:=20Unselect=20selection,=20when=20click=20on=20empty=20space?= =?UTF-8?q?=20=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 0000000000..8cd03aa06b --- /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 d4c81299fc..79c54748ac 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 16142ec05d..cc2c612fe9 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 6227c5130c..05eb02778e 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 1260ee3bbd..97714ea62a 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 2df337b043..fe8422865b 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 16433bc79e..594e0716c6 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 61d5bf6ece..4c592b8784 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 87be7a4e2e..bc9144803d 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 d09de9cf30..bb3f6e41a9 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 5b7e1c0677829bd84869818ca1e6207974394ec9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 29 Sep 2022 08:43:03 +0200 Subject: [PATCH 144/327] Measuring - GLGizmoMeasure - Allow to deselect second feature by clicking on it --- src/slic3r/GUI/GLCanvas3D.cpp | 51 ++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 83 +++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + src/slic3r/GUI/SceneRaycaster.cpp | 6 +- src/slic3r/GUI/SceneRaycaster.hpp | 3 +- 5 files changed, 108 insertions(+), 36 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ed82a3a8da..9c69003618 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5501,28 +5501,49 @@ void GLCanvas3D::_picking_pass() } default: { break; } } - char buf[1024]; + + auto add_strings_row_to_table = [&imgui](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(col_1_color, col_1.c_str()); + ImGui::TableSetColumnIndex(1); + imgui.text_colored(col_2_color, col_2.c_str()); + }; + if (hit.type != SceneRaycaster::EType::None) { - sprintf(buf, "Object ID: %d", hit.raycaster_id); - imgui.text(std::string(buf)); - sprintf(buf, "Type: %s", object_type.c_str()); - imgui.text(std::string(buf)); - sprintf(buf, "Position: %.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); - imgui.text(std::string(buf)); - sprintf(buf, "Normal: %.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); - imgui.text(std::string(buf)); + if (ImGui::BeginTable("Hit", 2)) { + char buf[1024]; + add_strings_row_to_table("Object ID:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::to_string(hit.raycaster_id), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + add_strings_row_to_table("Type:", ImGuiWrapper::COL_ORANGE_LIGHT, + object_type, ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); + add_strings_row_to_table("Position:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); + add_strings_row_to_table("Normal:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + ImGui::EndTable(); + } } else imgui.text("NO HIT"); ImGui::Separator(); imgui.text("Registered for picking:"); - sprintf(buf, "Beds: %d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); - imgui.text(std::string(buf)); - sprintf(buf, "Volumes: %d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); - imgui.text(std::string(buf)); - sprintf(buf, "Gizmo elements: %d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); - imgui.text(std::string(buf)); + if (ImGui::BeginTable("Counters", 2)) { + char buf[1024]; + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); + add_strings_row_to_table("Beds:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); + add_strings_row_to_table("Volumes:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); + add_strings_row_to_table("Gizmo elements:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + ImGui::EndTable(); + } imgui.end(); #endif // ENABLE_RAYCAST_PICKING_DEBUG } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c511f7e501..5dee42ad1e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -30,6 +30,8 @@ static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; +static const int SELECTION_1_ID = 501; +static const int SELECTION_2_ID = 502; static const float TRIANGLE_BASE = 10.0f; static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; @@ -47,11 +49,11 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty switch (type) { default: - case Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Measure::SurfaceFeatureType::Point: { return L("Vertex"); } - case Measure::SurfaceFeatureType::Edge: { return L("Edge"); } - case Measure::SurfaceFeatureType::Circle: { return L("Circle"); } - case Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + case Measure::SurfaceFeatureType::Undef: { return _u8L("Undefined"); } + case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return _u8L("Plane"); } } } @@ -277,20 +279,44 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto item_from_feature = [this]() { - const SelectedFeatures::Item item = { - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + SelectedFeatures::Item item; + if (m_hover_id == SELECTION_1_ID && m_selected_features.first.feature.has_value()) + item = m_selected_features.first; + else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value()) + item = m_selected_features.second; + else { + item = { + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature + }; + } return item; }; if (m_selected_features.first.feature.has_value()) { - const SelectedFeatures::Item item = item_from_feature(); - if (m_selected_features.first != item) - m_selected_features.second = item; - } - else - m_selected_features.first = item_from_feature(); + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); + if (it != m_selection_raycasters.end()) + m_selection_raycasters.erase(it); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID); + const SelectedFeatures::Item item = item_from_feature(); + if (m_selected_features.first != item) { + if (m_selected_features.second == item) + m_selected_features.second.reset(); + else { + m_selected_features.second = item; + if (m_mode == EMode::ExtendedSelection) + m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster)); + } + } + } + else { + const SelectedFeatures::Item item = item_from_feature(); + m_selected_features.first = item; + if (m_mode == EMode::ExtendedSelection) + m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); + } return true; } @@ -308,6 +334,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.RightDown() && mouse_event.CmdDown()) { m_selected_features.reset(); + m_selection_raycasters.clear(); m_imgui->set_requires_extra_frame(); } else if (mouse_event.Leaving()) @@ -332,6 +359,7 @@ void GLGizmoMeasure::data_changed() m_last_inv_zoom = 0.0f; m_last_plane_idx = -1; m_selected_features.reset(); + m_selection_raycasters.clear(); } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -435,7 +463,11 @@ void GLGizmoMeasure::on_render() std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { - GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + m_raycasters.clear(); m_curr_feature = curr_feature; if (!m_curr_feature.has_value()) return; @@ -687,11 +719,23 @@ void GLGizmoMeasure::on_render() std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); + if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); + if (it != m_selection_raycasters.end()) + (*it)->set_transform(m_volume_matrix * Geometry::translation_transform(m_selected_features.first.feature->get_point()) * Geometry::scale_transform(inv_zoom)); + } } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); + if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); + if (it != m_selection_raycasters.end()) + (*it)->set_transform(m_volume_matrix * Geometry::translation_transform(m_selected_features.second.feature->get_point()) * Geometry::scale_transform(inv_zoom)); + } } if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { @@ -1269,7 +1313,7 @@ void GLGizmoMeasure::render_debug_dialog() add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); }; - m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) m_imgui->text("Empty selection"); else { @@ -1323,7 +1367,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); }, [this]() { - m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), + m_selected_features.second.feature.has_value() ? + ((m_mode == EMode::BasicSelection) ? _u8L("Select/Unselect feature") : _u8L("Select/Unselect point")) : + ((m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"))); ImGui::SameLine(); const ImVec2 pos = ImGui::GetCursorScreenPos(); const float rect_size = ImGui::GetTextLineHeight(); @@ -1434,6 +1481,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit //if (m_selected_features.first.feature.has_value()) { // if (m_imgui->button(_u8L("Restart"))) { // m_selected_features.reset(); + // m_selection_raycasters.clear(); // m_imgui->set_requires_extra_frame(); // } //} @@ -1498,6 +1546,7 @@ void GLGizmoMeasure::on_unregister_raycasters_for_picking() m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); m_parent.set_raycaster_gizmos_on_top(false); m_raycasters.clear(); + m_selection_raycasters.clear(); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 0e967472ae..441d91c3a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,6 +87,7 @@ class GLGizmoMeasure : public GLGizmoBase Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; + std::vector> m_selection_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; struct SceneRaycasterState diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 78cb59c1bc..a92c622c1b 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -37,10 +37,10 @@ std::shared_ptr SceneRaycaster::add_raycaster(EType type, in const Transform3d& trafo, bool use_back_faces) { switch (type) { - case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Volume: { return m_volumes.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } - case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } - default: { assert(false); return nullptr; } + case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + default: { assert(false); return nullptr; } }; } diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 7acf82fe11..4a7eaab276 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -106,9 +106,10 @@ public: size_t active_gizmos_count() const; #endif // ENABLE_RAYCAST_PICKING_DEBUG + static int decode_id(EType type, int id); + private: static int encode_id(EType type, int id); - static int decode_id(EType type, int id); static int base_id(EType type); }; From ee088bf79877b18eed1b370e9d7419f13693aa70 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 29 Sep 2022 10:19:40 +0200 Subject: [PATCH 145/327] Measuring - GLGizmoMeasure - Added option to copy to clipboard the result of measurement --- src/imgui/imconfig.h | 16 +++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 57 ++++++++++++++++++++---- src/slic3r/GUI/ImGuiWrapper.cpp | 3 ++ 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index f2c3ef0837..7c79058c4c 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -156,6 +156,21 @@ namespace ImGui const wchar_t InfoMarker = 0x2603; const wchar_t SliderFloatEditBtnIcon = 0x2604; const wchar_t SliderFloatEditBtnPressedIcon = 0x2605; +#if ENABLE_MEASURE_GIZMO + const wchar_t ClipboardBtnIcon = 0x2606; + const wchar_t LegendTravel = 0x2701; + const wchar_t LegendWipe = 0x2702; + const wchar_t LegendRetract = 0x2703; + const wchar_t LegendDeretract = 0x2704; + const wchar_t LegendSeams = 0x2705; + const wchar_t LegendToolChanges = 0x2706; + const wchar_t LegendColorChanges = 0x2707; + const wchar_t LegendPausePrints = 0x2708; + const wchar_t LegendCustomGCodes = 0x2709; + const wchar_t LegendCOG = 0x2710; + const wchar_t LegendShells = 0x2711; + const wchar_t LegendToolMarker = 0x2712; +#else const wchar_t LegendTravel = 0x2606; const wchar_t LegendWipe = 0x2607; const wchar_t LegendRetract = 0x2608; @@ -168,6 +183,7 @@ namespace ImGui const wchar_t LegendCOG = 0x2615; const wchar_t LegendShells = 0x2616; const wchar_t LegendToolMarker = 0x2617; +#endif // ENABLE_MEASURE_GIZMO // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5dee42ad1e..c232405019 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -10,6 +10,8 @@ #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" +#include + #include #include @@ -1251,7 +1253,7 @@ static void add_row_to_table(std::function col_1 = nullptr, std::fun col_1(); ImGui::TableSetColumnIndex(1); col_2(); -}; +} static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { @@ -1263,14 +1265,14 @@ static std::string format_double(double value) char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); -}; +} static std::string format_vec3(const Vec3d& v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); return std::string(buf); -}; +} #if ENABLE_MEASURE_GIZMO_DEBUG void GLGizmoMeasure::render_debug_dialog() @@ -1486,36 +1488,75 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit // } //} + auto add_measure_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(col_1_color, col_1); + ImGui::TableSetColumnIndex(1); + m_imgui->text_colored(col_2_color, col_2); + ImGui::TableSetColumnIndex(2); + ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + 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.25f, 0.25f, 0.25f, 1.0f }); + if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + wxTheClipboard->Open(); + wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); + wxTheClipboard->Close(); + } + ImGui::PopStyleColor(3); + if (ImGui::IsItemHovered()) { + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); + } + }; + if (m_selected_features.second.feature.has_value()) { const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); - if (ImGui::BeginTable("Measure", 2)) { + if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - add_strings_row_to_table(*m_imgui, _u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + ImGui::PushID((void*)(intptr_t)1); + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_infinite.has_value()) { double distance = *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + ImGui::PushID((void*)(intptr_t)2); + add_measure_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_strict.has_value()) { double distance = *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + ImGui::PushID((void*)(intptr_t)3); + add_measure_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_xyz.has_value()) { Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + ImGui::PushID((void*)(intptr_t)4); + add_measure_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } ImGui::EndTable(); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index fa6e2c9031..4ce9dfefc0 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -56,6 +56,9 @@ static const std::map font_icons = { {ImGui::PreferencesHoverButton, "notification_preferences_hover"}, {ImGui::SliderFloatEditBtnIcon, "edit_button" }, {ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" }, +#if ENABLE_MEASURE_GIZMO + {ImGui::ClipboardBtnIcon , "copy_menu" }, +#endif // ENABLE_MEASURE_GIZMO #if ENABLE_LEGEND_TOOLBAR_ICONS {ImGui::LegendTravel , "legend_travel" }, {ImGui::LegendWipe , "legend_wipe" }, From d1c871758b72ec7a683df01de4907169978b4785 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 29 Sep 2022 12:26:08 +0200 Subject: [PATCH 146/327] 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 4251110c41..f2056b8bd6 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 cc2c612fe9..01cd374143 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 79f8e74c61..3ae0d05332 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 4c592b8784..0475fe395b 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 bc9144803d..55dbaafe2e 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 5ad4fafbf168631c12fd5e795eba4b7ecddaf83d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 11:22:23 +0200 Subject: [PATCH 147/327] Measurement: moving arrow-drawing functions from frontend to the backend (1/4) --- src/libslic3r/Measure.cpp | 24 +++-- src/libslic3r/Measure.hpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 126 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 4 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index c2b7edd5e2..a627fe62cc 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -349,7 +349,7 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const // which is needless and relatively expensive. res = get_measurement(plane.surface_features[i], point_sf); if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. - double dist = *res.distance_strict; + double dist = res.distance_strict->dist; if (dist < feature_hover_limit && dist < min_dist) { min_dist = std::min(dist, min_dist); closest_feature_idx = i; @@ -455,8 +455,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f1.get_type() == SurfaceFeatureType::Point) { if (f2.get_type() == SurfaceFeatureType::Point) { Vec3d diff = (f2.get_point() - f1.get_point()); - result.distance_strict = diff.norm(); + result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); result.distance_xyz = diff; + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { const auto& [s,e] = f2.get_edge(); @@ -468,11 +469,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(dist_inf); + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO } else { // the result is the closer of the endpoints - result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(dist_inf); + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -482,12 +484,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm()); - result.distance_strict = std::make_optional(dist); + const Vec3d p_on_circle = c + radius * (circle_plane.projection(f1.get_point()) - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); - result.distance_infinite = plane.absDistance(f1.get_point()); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = } /////////////////////////////////////////////////////////////////////////// @@ -495,18 +498,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// @@ -521,7 +529,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = plane.absDistance(pt2); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); } else { // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 5d71983f0b..6b7940f2bf 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -110,12 +110,16 @@ private: }; - +struct DistAndPoints { + double dist; + Vec3d from; + Vec3d to; +}; struct MeasurementResult { std::optional angle; - std::optional distance_infinite; - std::optional distance_strict; + std::optional distance_infinite; + std::optional distance_strict; std::optional distance_xyz; bool has_any_data() const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c232405019..11cd651e5c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -72,13 +72,6 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) -{ - const auto& [idx, normal, origin] = p; - const Eigen::Hyperplane plane(normal, origin); - return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); -} - static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); @@ -89,21 +82,13 @@ static Vec3d edge_direction(const Edge& e) return vector_direction(e.first, e.second); } -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) -{ - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - return std::make_tuple(line.distance(v), v, line.projection(v)); -} -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); - return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); -} + + + + +/* + // returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) @@ -165,6 +150,34 @@ static std::tuple distance_plane_plane(const Plane& p1, co return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); } +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + // returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar // After return, the edges are oriented so that they point away from their intersection point @@ -278,6 +291,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { + SelectedFeatures selected_features_old = m_selected_features; + + m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -319,6 +335,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_mode == EMode::ExtendedSelection) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); } + + if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + return true; } @@ -963,8 +983,11 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; + auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v_proj] = distance_point_edge(v, e); + const double distance = m_measurement_result.distance_infinite->dist; + const Vec3d v1 = m_measurement_result.distance_infinite->from; + const Vec3d v_proj = m_measurement_result.distance_infinite->to; point_point(v1, v_proj); const Vec3d e1e2 = e.second - e.first; @@ -997,16 +1020,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [point_point](const Vec3d& v, const Plane& p) { - const auto [distance, v1, v2] = distance_point_plane(v, p); - point_point(v1, v2); - }; - - auto point_circle = [point_point](const Vec3d& v, const Circle& c) { - const auto [distance, v1, v2] = distance_point_circle(v, c); - point_point(v1, v2); - }; - +/* auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { Edge e1copy = e1; Edge e2copy = e2; @@ -1130,7 +1144,7 @@ void GLGizmoMeasure::render_dimensioning() auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { const auto [distance, v1, v2] = distance_plane_plane(p1, p2); point_point(v1, v2); - }; + };*/ shader->start_using(); @@ -1174,26 +1188,26 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // point-point - if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + // Render the arrow between the points that the backend passed: + if (m_selected_features.second.feature.has_value()) { + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to); } + + // Now if one of the features is an edge, draw also the extension of the edge to where the dist is measured: + // TODO... + + // point-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); } - // point-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); - } - // point-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); - } + + + /* // edge-point else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { @@ -1214,11 +1228,6 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } // plane-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { @@ -1229,16 +1238,12 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } // circle-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); } +*/ glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1520,7 +1525,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); @@ -1532,7 +1538,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_infinite.has_value()) { - double distance = *measure.distance_infinite; + double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); @@ -1541,7 +1547,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_strict.has_value()) { - double distance = *measure.distance_strict; + double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 441d91c3a2..a5312ebf57 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -70,7 +70,9 @@ class GLGizmoMeasure : public GLGizmoBase }; EMode m_mode{ EMode::BasicSelection }; - std::unique_ptr m_measuring; + Measure::MeasurementResult m_measurement_result; + + std::unique_ptr m_measuring; // PIMPL PickingModel m_sphere; PickingModel m_cylinder; From 05c03e54d9172b235151f6dd2c7738ccb01b16f1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 12:33:20 +0200 Subject: [PATCH 148/327] Measurement: moving arrow-drawing functions from frontend to the backend (2/4) --- src/libslic3r/Measure.cpp | 35 +++++++++++++++++++++++- src/libslic3r/Measure.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index a627fe62cc..01f48579d2 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -498,7 +498,40 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + + std::vector distances; + + auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { + //const auto [distance, v1, v2] = distance_point_edge(v, SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); + + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + double distance = res.distance_strict->dist; + Vec3d v1 = res.distance_strict->from; + Vec3d v2 = res.distance_strict->to; + + + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(distance, v, v2); + }; + + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); + + distances.emplace_back((e2.first - e1.first).norm(), e1.first, e2.first); + distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); + distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); + distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(*it); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 6b7940f2bf..fb36b50095 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -111,6 +111,7 @@ private: struct DistAndPoints { + DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {} double dist; Vec3d from; Vec3d to; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 11cd651e5c..222d8087a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -90,7 +90,7 @@ static Vec3d edge_direction(const Edge& e) /* -// returns: distance, 1st vertex, 2nd vertex +// returns: distance, 1st vertex, 2nd vertexs static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; From 699a210c31b2b6fc912236e1e9fc0df2652a3507 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 14:50:29 +0200 Subject: [PATCH 149/327] Measurement: moving arrow-drawing functions from frontend to the backend (3/4) --- src/libslic3r/Measure.cpp | 98 ++++++- src/libslic3r/Measure.hpp | 25 +- src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 310 ++++------------------- 4 files changed, 156 insertions(+), 278 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 01f48579d2..fbf248c0f7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -431,6 +431,67 @@ std::vector> Measuring::get_planes_triangle_indices() const +static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +{ + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); +} + @@ -469,12 +530,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints bool s_is_closer = dist_start_sq < dist_end_sq; result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -498,18 +559,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { - std::vector distances; auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - //const auto [distance, v1, v2] = distance_point_edge(v, SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); double distance = res.distance_strict->dist; - Vec3d v1 = res.distance_strict->from; Vec3d v2 = res.distance_strict->to; - const Vec3d e1e2 = e.second - e.first; const Vec3d e1v2 = v2 - e.first; if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) @@ -531,10 +587,32 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& [](const DistAndPoints& item1, const DistAndPoints& item2) { return item1.dist < item2.dist; }); - result.distance_infinite = std::make_optional(*it); // TODO + result.distance_infinite = std::make_optional(*it); + + result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge()); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + const std::pair e = f1.get_edge(); + const auto& [center, radius, normal] = f2.get_circle(); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + + std::vector distances; + distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); + distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict); + + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO @@ -567,7 +645,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = angle; + result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index fb36b50095..b88411a9f6 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -15,12 +15,12 @@ namespace Slic3r { namespace Measure { -enum class SurfaceFeatureType { - Undef, - Point, - Edge, - Circle, - Plane +enum class SurfaceFeatureType : int { + Undef = 0, + Point = 1 << 0, + Edge = 1 << 1, + Circle = 1 << 2, + Plane = 1 << 3 }; class SurfaceFeature { @@ -117,8 +117,19 @@ struct DistAndPoints { Vec3d to; }; +struct AngleAndPoints { + AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} + double angle; + Vec3d center; + Vec3d e1; + Vec3d e2; + double radius; + bool coplanar; +}; + struct MeasurementResult { - std::optional angle; + std::optional angle; std::optional distance_infinite; std::optional distance_strict; std::optional distance_xyz; diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index a4b261ceb9..9e547eec49 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -2,6 +2,7 @@ #define slic3r_SurfaceMesh_hpp_ #include +#include namespace Slic3r { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 222d8087a0..e9fbd7f8c0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,15 +16,13 @@ #include +#include + #if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace GUI { -using Edge = std::pair; -using Plane = std::tuple; -using Circle = std::tuple; - static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; @@ -72,176 +70,9 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) +static Vec3d edge_direction(const std::pair& e) { - return (to - from).normalized(); -} - -static Vec3d edge_direction(const Edge& e) -{ - return vector_direction(e.first, e.second); -} - - - - - - -/* - - -// returns: distance, 1st vertex, 2nd vertexs -static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) -{ - std::vector> distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v2] = distance_point_edge(v, e); - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(std::make_tuple(distance, v, v2)); - }; - - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_edge_circle(const Edge& e, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = vector_direction(e.first, e.second); - - std::vector> distances; - distances.emplace_back(distance_point_circle(e.first, c)); - distances.emplace_back(distance_point_circle(e.second, c)); - - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inter = line.intersectionPoint(plane); - const Vec3d e1inter = inter - e.first; - if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) - distances.emplace_back(distance_point_circle(inter, c)); - - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} -*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar -// After return, the edges are oriented so that they point away from their intersection point -static std::tuple angle_edge_edge(Edge& e1, Edge& e2) -{ - Vec3d e1_unit = edge_direction(e1); - Vec3d e2_unit = edge_direction(e2); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - return std::make_tuple(0.0, e1.first, 0.0, true); - - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); - - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; - - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection (arc center) of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // arc center in original coordinate - const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // ensure the edges are pointing away from the center - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - - return std::make_tuple(angle, center, radius, coplanar); + return (e.second - e.first).normalized(); } static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) @@ -984,11 +815,9 @@ void GLGizmoMeasure::render_dimensioning() }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const double distance = m_measurement_result.distance_infinite->dist; - const Vec3d v1 = m_measurement_result.distance_infinite->from; + auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; - point_point(v1, v_proj); const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; @@ -1020,21 +849,30 @@ void GLGizmoMeasure::render_dimensioning() } }; -/* - auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { - Edge e1copy = e1; - Edge e2copy = e2; - const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double* const force_radius = nullptr) { + Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const double angle = res.angle->angle; + const Vec3d center = res.angle->center; + const Vec3d e1_unit = res.angle->e1; + const Vec3d e2_unit = res.angle->e2; + const double radius = res.angle->radius; + const bool coplanar = res.angle->coplanar; - if (radius == 0.0) + std::pair e1 = m_selected_features.first.feature->get_edge(); + std::pair e2 = m_selected_features.second.feature->get_edge(); + + if ((e1.second - e1.first).dot(e1_unit) < 0.) + std::swap(e1.first, e1.second); + if ((e2.second - e2.first).dot(e2_unit) < 0.) + std::swap(e2.first, e2.second); + + if (radius == 0.) return; assert(force_radius == nullptr || *force_radius > 0.0); - const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; + double draw_radius = force_radius ? *force_radius : radius; - const Vec3d e1_unit = edge_direction(e1copy); - const Vec3d e2_unit = edge_direction(e2copy); const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { @@ -1064,31 +902,33 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.render(); // render edge 1 extension - const Vec3d e11e12 = e1copy.second - e1copy.first; - const Vec3d e11center = center - e1copy.first; + const Vec3d e11e12 = e1.second - e1.first; + const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } // render edge 2 extension - const Vec3d e21center = center - e2copy.first; + const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; + +/* auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; @@ -1122,29 +962,7 @@ void GLGizmoMeasure::render_dimensioning() const double radius = (inters - e1e2copy_mid).norm(); arc_edge_edge(ecopy, edge_on_plane, &radius); }; - - auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { - // distance - const auto [distance, v1, v2] = distance_edge_edge(e1, e2); - point_point(v1, v2); - // arc - arc_edge_edge(e1, e2); - }; - - auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { - // arc - arc_edge_plane(e, p); - }; - - auto edge_circle = [point_point](const Edge& e, const Circle& c) { - const auto [distance, v1, v2] = distance_edge_circle(e, c); - point_point(v1, v2); - }; - - auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { - const auto [distance, v1, v2] = distance_plane_plane(p1, p2); - point_point(v1, v2); - };*/ +*/ shader->start_using(); @@ -1188,63 +1006,33 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // Render the arrow between the points that the backend passed: if (m_selected_features.second.feature.has_value()) { + // Render the arrow between the points that the backend passed: const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; point_point(dap.from, dap.to); - } - // Now if one of the features is an edge, draw also the extension of the edge to where the dist is measured: - // TODO... + const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; + const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; + Measure::SurfaceFeatureType ft1 = f1.get_type(); + Measure::SurfaceFeatureType ft2 = f2.get_type(); - // point-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); + // Where needed, draw also the extension of the edge to where the dist is measured: + if ((int(ft1) | int(ft2)) == + (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) + point_edge(f1, f2); + + + // Now if there is an angle to show, draw the arc: + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) + arc_edge_edge(f1, f2); + //if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + // arc_edge_plane(); } - /* - // edge-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } - // edge-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); - } - // edge-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); - } - // edge-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); - } - // plane-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); - } - // plane-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); - } - // circle-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); - } -*/ - glsafe(::glEnable(GL_DEPTH_TEST)); shader->stop_using(); @@ -1533,7 +1321,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } From 5091d77adb9083152e0b92da515e3b15f9ead8be Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 16:39:39 +0200 Subject: [PATCH 150/327] Measurement: moving arrow-drawing functions from frontend to the backend (4/4) --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e9fbd7f8c0..568d5db3a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -849,7 +849,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double* const force_radius = nullptr) { + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; @@ -928,8 +928,12 @@ void GLGizmoMeasure::render_dimensioning() }; -/* - auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); + const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; const double abs_dot = std::abs(normal.dot(edge_direction(e))); @@ -941,7 +945,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d inters = line.intersectionPoint(plane); // ensure the edge is pointing away from the intersection - Edge ecopy = e; + std::pair ecopy = e; Vec3d e1e2copy = e1e2; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { std::swap(ecopy.first, ecopy.second); @@ -951,7 +955,7 @@ void GLGizmoMeasure::render_dimensioning() // calculate 2nd edge (on the plane) const Vec3d temp = normal.cross(e1e2copy); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); @@ -960,9 +964,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(ecopy, edge_on_plane, &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), + &radius); }; -*/ + shader->start_using(); @@ -1028,8 +1034,8 @@ void GLGizmoMeasure::render_dimensioning() // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) arc_edge_edge(f1, f2); - //if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) - // arc_edge_plane(); + if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + arc_edge_plane(f1, f2); } From 97e69c04f87a4b3d6d34b406c56808b8e04f27e5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 30 Sep 2022 08:52:38 +0200 Subject: [PATCH 151/327] Fixed crashing asserts due to a bug in the just merged branch --- src/libslic3r/Measure.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 26 +++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index fbf248c0f7..78d3d524e7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -646,6 +646,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& angle = std::acos(std::abs(normal1.dot(normal2))); } result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 568d5db3a2..6aebd0fa37 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -858,8 +858,8 @@ void GLGizmoMeasure::render_dimensioning() const double radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - std::pair e1 = m_selected_features.first.feature->get_edge(); - std::pair e2 = m_selected_features.second.feature->get_edge(); + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); if ((e1.second - e1.first).dot(e1_unit) < 0.) std::swap(e1.first, e1.second); @@ -1019,23 +1019,25 @@ void GLGizmoMeasure::render_dimensioning() : *m_measurement_result.distance_strict; point_point(dap.from, dap.to); - const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; - const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; - Measure::SurfaceFeatureType ft1 = f1.get_type(); - Measure::SurfaceFeatureType ft2 = f2.get_type(); + const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); + const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); + Measure::SurfaceFeatureType ft1 = f1->get_type(); + Measure::SurfaceFeatureType ft2 = f2->get_type(); + // Order features by type so following conditions are simple. + if (ft2 > ft2) + std::swap(ft1, ft2); // Where needed, draw also the extension of the edge to where the dist is measured: - if ((int(ft1) | int(ft2)) == - (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) - point_edge(f1, f2); + if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + point_edge(*f1, *f2); // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) - arc_edge_edge(f1, f2); - if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) - arc_edge_plane(f1, f2); + arc_edge_edge(*f1, *f2); + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + arc_edge_plane(*f1, *f2); } From 9b915bdd0985fd3deb7b99f4d3598b63fa5b3b99 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 09:44:22 +0200 Subject: [PATCH 152/327] Measuring - GLGizmoMeasure - Visualization and selection of extra point for edges --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6aebd0fa37..76d2dacd64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -62,7 +62,7 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t std::string ret; switch (type) { case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = (hover_id == POINT_ID) ? _u8L("Center of edge") : _u8L("Point on edge"); break; } case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } default: { assert(false); break; } @@ -335,6 +335,8 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + if (m_curr_feature->get_extra_point().has_value()) + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; } case Measure::SurfaceFeatureType::Circle: @@ -400,7 +402,11 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value() && m_hover_id == POINT_ID) + m_curr_point_on_feature_position = *extra; + else + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: @@ -497,11 +503,25 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const auto& [start, end] = feature.get_edge(); + // render extra point + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + // render edge const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(colors.front()); + m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); if (update_raycasters) { auto it = m_raycasters.find(EDGE_ID); @@ -550,13 +570,13 @@ void GLGizmoMeasure::on_render() colors.emplace_back(hover_selection_color()); break; } + case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Circle: { colors.emplace_back((m_hover_id == POINT_ID) ? hover_selection_color() : hovering_color()); colors.emplace_back(hovering_color()); break; } - case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Plane: { colors.emplace_back(hovering_color()); From f7afed4bd3ba70339e77ee3b87b398ec2a1d3a16 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 12:06:07 +0200 Subject: [PATCH 153/327] Follow-up of 9b915bdd0985fd3deb7b99f4d3598b63fa5b3b99 - Fixed crash --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 76d2dacd64..5cc4ce24e3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -504,7 +504,7 @@ void GLGizmoMeasure::on_render() { const auto& [start, end] = feature.get_edge(); // render extra point - const std::optional extra = m_curr_feature->get_extra_point(); + const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); From 8111fdfb544c139f404312533252a238a222b9dc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 12:54:00 +0200 Subject: [PATCH 154/327] Measuring - Added a bunch of utility functions in Measure.hpp --- src/libslic3r/Measure.cpp | 6 ++--- src/libslic3r/Measure.hpp | 32 ++++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 21 ++++------------ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 78d3d524e7..b27425ea91 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -633,11 +633,11 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } else if (f1.get_type() == SurfaceFeatureType::Plane) { assert(f2.get_type() == SurfaceFeatureType::Plane); - const auto& [idx1, normal1, pt1] = f1.get_plane(); - const auto& [idx2, normal2, pt2] = f2.get_plane(); + const auto [idx1, normal1, pt1] = f1.get_plane(); + const auto [idx2, normal2, pt2] = f2.get_plane(); double angle = 0.; - if (normal1.isApprox(normal2)) { + if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index b88411a9f6..4ac56a87f3 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -142,6 +142,38 @@ struct MeasurementResult { // Returns distance/angle between two SurfaceFeatures. MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const SurfaceFeature& edge) { + assert(edge.get_type() == SurfaceFeatureType::Edge); + const auto [from, to] = edge.get_edge(); + return edge_direction(from, to); +} + +inline Vec3d plane_normal(const SurfaceFeature& plane) { + assert(plane.get_type() == SurfaceFeatureType::Plane); + return std::get<1>(plane.get_plane()); +} + +inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } +inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } + +inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_parallel(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_perpendicular(edge_direction(f1), plane_normal(f2)); + else + return false; +} + +inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_perpendicular(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_parallel(edge_direction(f1), plane_normal(f2)); + else + return false; +} } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5cc4ce24e3..421dfbedd5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -70,11 +70,6 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static Vec3d edge_direction(const std::pair& e) -{ - return (e.second - e.first).normalized(); -} - static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -123,8 +118,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { SelectedFeatures selected_features_old = m_selected_features; - - m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -834,7 +827,6 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; @@ -929,7 +921,7 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } @@ -941,14 +933,12 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; - - auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); @@ -956,8 +946,8 @@ void GLGizmoMeasure::render_dimensioning() const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; - const double abs_dot = std::abs(normal.dot(edge_direction(e))); - if (abs_dot < EPSILON || std::abs(abs_dot - 1.0) < EPSILON) + const double abs_dot = std::abs(normal.dot(Measure::edge_direction(f1))); + if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; const Eigen::Hyperplane plane(normal, origin); @@ -1052,11 +1042,10 @@ void GLGizmoMeasure::render_dimensioning() if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) point_edge(*f1, *f2); - // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) arc_edge_edge(*f1, *f2); - if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); } From 0ef811556408ecbf0d41058563d51b8c86166005 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 13:18:17 +0200 Subject: [PATCH 155/327] Measuring - Refactoring and bug fixing in GLGizmoMeasure::render_dimensioning() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 36 +++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 421dfbedd5..9a62a8c39f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -828,7 +828,8 @@ void GLGizmoMeasure::render_dimensioning() }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { - std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + assert(f1.get_type() == Measure::SurfaceFeatureType::Point && f2.get_type() == Measure::SurfaceFeatureType::Edge); + const std::pair e = f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; const Vec3d e1e2 = e.second - e.first; @@ -862,7 +863,8 @@ void GLGizmoMeasure::render_dimensioning() }; auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { - Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; const Vec3d e1_unit = res.angle->e1; @@ -940,13 +942,9 @@ void GLGizmoMeasure::render_dimensioning() }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { - - std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); - std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); - - const auto& [idx, normal, origin] = p; - const Vec3d e1e2 = e.second - e.first; - const double abs_dot = std::abs(normal.dot(Measure::edge_direction(f1))); + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); + std::pair e = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; @@ -956,21 +954,20 @@ void GLGizmoMeasure::render_dimensioning() // ensure the edge is pointing away from the intersection std::pair ecopy = e; - Vec3d e1e2copy = e1e2; - if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { + if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) std::swap(ecopy.first, ecopy.second); - e1e2copy = -e1e2copy; - } // calculate 2nd edge (on the plane) - const Vec3d temp = normal.cross(e1e2copy); + const Vec3d e1e2 = ecopy.second - ecopy.first; + const double e1e2_len = e1e2.norm(); + const Vec3d temp = normal.cross(e1e2); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction - const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); if (test_edge.dot(temp) < 0.0) - edge_on_plane = { origin, origin - e1e2copy.norm() * edge_on_plane_unit }; + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); @@ -1035,8 +1032,10 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeatureType ft2 = f2->get_type(); // Order features by type so following conditions are simple. - if (ft2 > ft2) + if (ft1 > ft2) { std::swap(ft1, ft2); + std::swap(f1, f2); + } // Where needed, draw also the extension of the edge to where the dist is measured: if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) @@ -1048,7 +1047,6 @@ void GLGizmoMeasure::render_dimensioning() else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); } - glsafe(::glEnable(GL_DEPTH_TEST)); From a8440db5ec6cff6319a8bb7b9824a416d1414be4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 30 Sep 2022 14:07:17 +0200 Subject: [PATCH 156/327] 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 05eb02778e..d3f1d1bf27 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 3ae0d05332..cb498f87ef 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 e54a4cf56d..a2d687db01 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 97714ea62a..71a9c5dcf7 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 fe8422865b..4335da35ae 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 dbe0ba7346..12c783c431 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 99d5eb8814..80d6f1aa05 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 9adfa2241e..9b49cf0b4e 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 c6ddd1fa3e..a3828f53df 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 cebea29e05..97a85a2dcc 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 6be56413f6ad36d0fa5aebc5d3d72b5f501018b1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 14:13:17 +0200 Subject: [PATCH 157/327] Measuring - Some refactoring --- src/libslic3r/Measure.cpp | 21 ++++++++++----------- src/libslic3r/Measure.hpp | 7 +++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index b27425ea91..ff07aa0810 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -250,7 +250,7 @@ void MeasuringImpl::extract_features() // Add the circle and remember indices into borders. const auto& [center, radius] = get_center_and_radius(border, start_idx, i, trafo); circles_idxs.emplace_back(start_idx, i); - circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius)); circle = false; } } @@ -269,7 +269,7 @@ void MeasuringImpl::extract_features() const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, - border[j-1], border[j], std::make_optional(center), 0.)); + border[j - 1], border[j], std::make_optional(center))); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +288,8 @@ void MeasuringImpl::extract_features() for (int i=1; i (int)circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(SurfaceFeature( - SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); + else + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i - 1], border[i])); } // FIXME Throw away / do not create edges which are parts of circles or @@ -307,7 +307,7 @@ void MeasuringImpl::extract_features() // The last surface feature is the plane itself. plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - plane.normal, plane.borders.front().front(), std::optional(), i + 0.0001)); + plane.normal, plane.borders.front().front(), std::nullopt, i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -433,13 +433,12 @@ std::vector> Measuring::get_planes_triangle_indices() const static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) { - Vec3d e1_unit = (e1.second - e1.first).normalized(); - Vec3d e2_unit = (e2.second - e2.first).normalized(); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) + if (are_parallel(e1, e2)) return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + Vec3d e1_unit = edge_direction(e1.first, e1.second); + Vec3d e2_unit = edge_direction(e2.first, e2.second); + // project edges on the plane defined by them Vec3d normal = e1_unit.cross(e2_unit).normalized(); const Eigen::Hyperplane plane(normal, e1.first); @@ -562,7 +561,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& std::vector distances; auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); double distance = res.distance_strict->dist; Vec3d v2 = res.distance_strict->to; diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 4ac56a87f3..1c51289e8d 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -25,8 +25,8 @@ enum class SurfaceFeatureType : int { class SurfaceFeature { public: - SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) - : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) + : m_type{ type }, m_pt1{ pt1 }, m_pt2{ pt2 }, m_pt3{ pt3 }, m_value{ value } {} explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} @@ -157,6 +157,9 @@ inline Vec3d plane_normal(const SurfaceFeature& plane) { inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } +inline bool are_parallel(const std::pair& e1, const std::pair& e2) { + return are_parallel(e1.second - e1.first, e2.second - e2.first); +} inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) return are_parallel(edge_direction(f1), edge_direction(f2)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9a62a8c39f..d6488b83a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -887,8 +887,6 @@ void GLGizmoMeasure::render_dimensioning() double draw_radius = force_radius ? *force_radius : radius; - const Vec3d normal = e1_unit.cross(e2_unit).normalized(); - if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; @@ -898,6 +896,7 @@ void GLGizmoMeasure::render_dimensioning() init_data.reserve_indices(resolution + 1); // vertices + indices + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); @@ -943,16 +942,19 @@ void GLGizmoMeasure::render_dimensioning() auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - std::pair e = f1.get_edge(); - const auto [idx, normal, origin] = f2.get_plane(); if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; + const std::pair e = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane const Eigen::Hyperplane plane(normal, origin); const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); const Vec3d inters = line.intersectionPoint(plane); - // ensure the edge is pointing away from the intersection + // then verify edge direction and revert it, if needed std::pair ecopy = e; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) std::swap(ecopy.first, ecopy.second); @@ -971,9 +973,9 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), - &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.first, ecopy.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.first, edge_on_plane.second), + &radius); }; From 7cdc7ac5357c6a89fb2fa1bde0f2000346c5a8b1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 14:35:39 +0200 Subject: [PATCH 158/327] Measuring - struct AngleAndPoints reworked as struct AngleAndEdges --- src/libslic3r/Measure.cpp | 14 +++++++------- src/libslic3r/Measure.hpp | 20 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++---------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index ff07aa0810..b9fb0f3448 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -427,14 +427,12 @@ std::vector> Measuring::get_planes_triangle_indices() const } +const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; - - - -static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +static AngleAndEdges angle_edge_edge(std::pair e1, std::pair e2) { if (are_parallel(e1, e2)) - return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + return AngleAndEdges::Dummy; Vec3d e1_unit = edge_direction(e1.first, e1.second); Vec3d e2_unit = edge_direction(e2.first, e2.second); @@ -474,10 +472,12 @@ static AngleAndPoints angle_edge_edge(const std::pair& e1, const s // ensure the edges are pointing away from the center if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { std::swap(e11_proj, e12_proj); + std::swap(e1.first, e1.second); e1_unit = -e1_unit; } if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { std::swap(e21_proj, e22_proj); + std::swap(e2.first, e2.second); e2_unit = -e2_unit; } @@ -488,7 +488,7 @@ static AngleAndPoints angle_edge_edge(const std::pair& e1, const s const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); + return { angle, center, e1, e2, radius, coplanar }; } @@ -644,7 +644,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO + result.angle = std::make_optional(AngleAndEdges(angle, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0., false)); // TODO result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1c51289e8d..c59c9bbbc7 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -26,7 +26,7 @@ enum class SurfaceFeatureType : int { class SurfaceFeature { public: SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) - : m_type{ type }, m_pt1{ pt1 }, m_pt2{ pt2 }, m_pt3{ pt3 }, m_value{ value } {} + : m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {} explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} @@ -117,19 +117,21 @@ struct DistAndPoints { Vec3d to; }; -struct AngleAndPoints { - AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) - : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} +struct AngleAndEdges { + AngleAndEdges(double angle_, const Vec3d& center_, const std::pair& e1_, const std::pair& e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} double angle; Vec3d center; - Vec3d e1; - Vec3d e2; + std::pair e1; + std::pair e2; double radius; bool coplanar; + + static const AngleAndEdges Dummy; }; struct MeasurementResult { - std::optional angle; + std::optional angle; std::optional distance_infinite; std::optional distance_strict; std::optional distance_xyz; @@ -143,10 +145,10 @@ struct MeasurementResult { MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } inline Vec3d edge_direction(const SurfaceFeature& edge) { assert(edge.get_type() == SurfaceFeatureType::Edge); - const auto [from, to] = edge.get_edge(); - return edge_direction(from, to); + return edge_direction(edge.get_edge()); } inline Vec3d plane_normal(const SurfaceFeature& plane) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d6488b83a9..138e8c54a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -867,19 +867,11 @@ void GLGizmoMeasure::render_dimensioning() const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; - const Vec3d e1_unit = res.angle->e1; - const Vec3d e2_unit = res.angle->e2; + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; const double radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - std::pair e1 = f1.get_edge(); - std::pair e2 = f2.get_edge(); - - if ((e1.second - e1.first).dot(e1_unit) < 0.) - std::swap(e1.first, e1.second); - if ((e2.second - e2.first).dot(e2_unit) < 0.) - std::swap(e2.first, e2.second); - if (radius == 0.) return; @@ -887,6 +879,9 @@ void GLGizmoMeasure::render_dimensioning() double draw_radius = force_radius ? *force_radius : radius; + const Vec3d e1_unit = Measure::edge_direction(e1); + const Vec3d e2_unit = Measure::edge_direction(e2); + if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; From 6f63a69e04b507749e056d58d4de3d511cd8c678 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 15:50:31 +0200 Subject: [PATCH 159/327] Measuring - Calculation of angle between edge and plane moved to backend --- src/libslic3r/Measure.cpp | 48 ++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 52 ++++++------------------ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index b9fb0f3448..f2cbb9f640 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -429,7 +429,7 @@ std::vector> Measuring::get_planes_triangle_indices() const const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; -static AngleAndEdges angle_edge_edge(std::pair e1, std::pair e2) +static AngleAndEdges angle_edge_edge(const std::pair& e1, const std::pair& e2) { if (are_parallel(e1, e2)) return AngleAndEdges::Dummy; @@ -470,14 +470,16 @@ static AngleAndEdges angle_edge_edge(std::pair e1, std::pair out_e1 = e1; + std::pair out_e2 = e2; if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { std::swap(e11_proj, e12_proj); - std::swap(e1.first, e1.second); + std::swap(out_e1.first, out_e1.second); e1_unit = -e1_unit; } if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { std::swap(e21_proj, e22_proj); - std::swap(e2.first, e2.second); + std::swap(out_e2.first, out_e2.second); e2_unit = -e2_unit; } @@ -488,9 +490,46 @@ static AngleAndEdges angle_edge_edge(std::pair e1, std::pair& e, const std::tuple& p) +{ + const auto& [idx, normal, origin] = p; + const Vec3d e1e2_unit = edge_direction(e); + if (are_parallel(e1e2_unit, normal) || are_perpendicular(e1e2_unit, normal)) + return AngleAndEdges::Dummy; + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // then verify edge direction and revert it, if needed + Vec3d e1 = e.first; + Vec3d e2 = e.second; + if ((e1 - inters).squaredNorm() > (e2 - inters).squaredNorm()) + std::swap(e1, e2); + + const Vec3d e1e2 = e2 - e1; + const double e1e2_len = e1e2.norm(); + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; + + AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); + const Vec3d e1e2copy_mid = 0.5 * (e1 + e2); + ret.radius = (inters - e1e2copy_mid).norm(); + return ret; +} @@ -615,6 +654,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 138e8c54a3..db015bf1c2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -862,22 +862,20 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; const std::pair e1 = res.angle->e1; const std::pair e2 = res.angle->e2; - const double radius = res.angle->radius; + const double calc_radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - if (radius == 0.) + if (calc_radius == 0.0) return; - assert(force_radius == nullptr || *force_radius > 0.0); - - double draw_radius = force_radius ? *force_radius : radius; + const double draw_radius = (radius > 0.0) ? radius : calc_radius; const Vec3d e1_unit = Measure::edge_direction(e1); const Vec3d e2_unit = Measure::edge_direction(e2); @@ -930,50 +928,24 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * - Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); + Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; + const double calc_radius = res.angle->radius; + if (calc_radius == 0.0) return; - const std::pair e = f1.get_edge(); - const auto [idx, normal, origin] = f2.get_plane(); - - // ensure the edge is pointing away from the intersection - // 1st calculate instersection between edge and plane - const Eigen::Hyperplane plane(normal, origin); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inters = line.intersectionPoint(plane); - - // then verify edge direction and revert it, if needed - std::pair ecopy = e; - if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) - std::swap(ecopy.first, ecopy.second); - - // calculate 2nd edge (on the plane) - const Vec3d e1e2 = ecopy.second - ecopy.first; - const double e1e2_len = e1e2.norm(); - const Vec3d temp = normal.cross(e1e2); - const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; - - // ensure the 2nd edge is pointing in the correct direction - const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); - if (test_edge.dot(temp) < 0.0) - edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; - - const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); - const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.first, ecopy.second), - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.first, edge_on_plane.second), - &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - shader->start_using(); if (!m_dimensioning.line.is_initialized()) { From edc90170f4f79bd4200375ccbbd29f7e5e8c31cf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 09:14:47 +0200 Subject: [PATCH 160/327] Measuring - Gizmo measure shows dimensioning for angle plane-plane --- src/libslic3r/Measure.cpp | 54 ++++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index f2cbb9f640..4d37ea712c 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -306,8 +306,17 @@ void MeasuringImpl::extract_features() } // The last surface feature is the plane itself. + Vec3d cog = Vec3d::Zero(); + size_t counter = 0; + for (const std::vector& b : plane.borders) { + for (size_t i = 1; i < b.size(); ++i) { + cog += b[i]; + ++counter; + } + } + cog /= double(counter); plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - plane.normal, plane.borders.front().front(), std::nullopt, i + 0.0001)); + plane.normal, cog, std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -526,11 +535,48 @@ static AngleAndEdges angle_edge_plane(const std::pair& e, const st edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); - const Vec3d e1e2copy_mid = 0.5 * (e1 + e2); - ret.radius = (inters - e1e2copy_mid).norm(); + ret.radius = (inters - 0.5 * (e1 + e2)).norm(); return ret; } +static AngleAndEdges angle_plane_plane(const std::tuple& p1, const std::tuple& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + + // are planes parallel ? + if (are_parallel(normal1, normal2)) + return AngleAndEdges::Dummy; + + auto intersection_plane_plane = [](const Vec3d& n1, const Vec3d& o1, const Vec3d& n2, const Vec3d& o2) { + Eigen::MatrixXd m(2, 3); + m << n1.x(), n1.y(), n1.z(), n2.x(), n2.y(), n2.z(); + Eigen::VectorXd b(2); + b << o1.dot(n1), o2.dot(n2); + Eigen::VectorXd x = m.colPivHouseholderQr().solve(b); + return std::make_pair(n1.cross(n2).normalized(), Vec3d(x(0), x(1), x(2))); + }; + + // Calculate intersection line between planes + const auto [intersection_line_direction, intersection_line_origin] = intersection_plane_plane(normal1, origin1, normal2, origin2); + + // Project planes' origin on intersection line + const Eigen::ParametrizedLine intersection_line = Eigen::ParametrizedLine(intersection_line_origin, intersection_line_direction); + const Vec3d origin1_proj = intersection_line.projection(origin1); + const Vec3d origin2_proj = intersection_line.projection(origin2); + + // Calculate edges on planes + const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); + const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); + const double edges_angle = std::acos(std::clamp(edge_on_plane1_unit.dot(edge_on_plane2_unit), -1.0, 1.0)); + const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); + const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; + const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; + + AngleAndEdges ret = angle_edge_edge(edge_on_plane1, edge_on_plane2); + ret.radius = radius; + return ret; +} @@ -684,7 +730,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = std::make_optional(AngleAndEdges(angle, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0., false)); // TODO + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db015bf1c2..b790e43306 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -946,6 +946,19 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; + const double calc_radius = res.angle->radius; + if (calc_radius == 0.0) + return; + + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -1015,6 +1028,8 @@ void GLGizmoMeasure::render_dimensioning() arc_edge_edge(*f1, *f2); else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); + else if (ft1 == Measure::SurfaceFeatureType::Plane && ft2 == Measure::SurfaceFeatureType::Plane) + arc_plane_plane(*f1, *f2); } glsafe(::glEnable(GL_DEPTH_TEST)); From 2e81200db039e0bf3c895f9a9984badb703a4af9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 10:50:38 +0200 Subject: [PATCH 161/327] Measuring - Gizmo measure shows value of distance dimensioning in 3D scene --- src/libslic3r/Measure.cpp | 8 ++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 51 ++++++++++++++++-------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 4d37ea712c..529c6f1231 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -663,10 +663,10 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); +// add_point_edge_distance(e1.first, e2); +// add_point_edge_distance(e1.second, e2); +// add_point_edge_distance(e2.first, e1); +// add_point_edge_distance(e2.second, e1); auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints& item1, const DistAndPoints& item2) { return item1.dist < item2.dist; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b790e43306..4efaf67d78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -44,6 +44,20 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +} + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +} + static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) { switch (type) @@ -778,7 +792,7 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2) { + auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2, float distance) { if (v1.isApprox(v2)) return; @@ -825,6 +839,23 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + + if (distance > 0.0) { + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + std::string txt = format_double(distance) + " "; + if (wxGetApp().app_config->get("use_inches") == "1") + txt += _u8L("in"); + else + txt += _u8L("mm"); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); + } }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -872,7 +903,7 @@ void GLGizmoMeasure::render_dimensioning() const double calc_radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - if (calc_radius == 0.0) + if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) return; const double draw_radius = (radius > 0.0) ? radius : calc_radius; @@ -1006,7 +1037,7 @@ void GLGizmoMeasure::render_dimensioning() const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to); + point_point(dap.from, dap.to, dap.dist); const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); @@ -1052,20 +1083,6 @@ static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); }; -static std::string format_double(double value) -{ - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); -} - -static std::string format_vec3(const Vec3d& v) -{ - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); -} - #if ENABLE_MEASURE_GIZMO_DEBUG void GLGizmoMeasure::render_debug_dialog() { From 1f5859deb1af3fb1e5b76cc29434c257df63c991 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 11:36:08 +0200 Subject: [PATCH 162/327] Measuring - Gizmo measure shows value of angle dimensioning in 3D scene --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 63 ++++++++++++++---------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4efaf67d78..8a4cd17d94 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -840,22 +840,18 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); - if (distance > 0.0) { - const Vec2d label_position = 0.5 * (v1ss + v2ss); - m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); - m_imgui->set_next_window_bg_alpha(0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - std::string txt = format_double(distance) + " "; - if (wxGetApp().app_config->get("use_inches") == "1") - txt += _u8L("in"); - else - txt += _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); - } + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : + format_double(distance) + " " + _u8L("mm"); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -911,8 +907,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1_unit = Measure::edge_direction(e1); const Vec3d e2_unit = Measure::edge_direction(e2); + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + const double step = angle / double(resolution); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); + if (!m_dimensioning.arc.is_initialized()) { - const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; init_data.color = ColorRGBA::WHITE(); @@ -920,8 +919,6 @@ void GLGizmoMeasure::render_dimensioning() init_data.reserve_indices(resolution + 1); // vertices + indices - const Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); @@ -943,8 +940,6 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); @@ -955,13 +950,29 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } + + // world coordinates + const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + + // screen coordinates + const std::array& viewport = camera.get_viewport(); + const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_world, m_volume_matrix, + camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); + + m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1341,7 +1352,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_infinite.has_value()) { + if (measure.distance_infinite.has_value() && measure.distance_infinite->dist > 0.0) { double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1350,7 +1361,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_strict.has_value()) { + if (measure.distance_strict.has_value() && measure.distance_strict->dist > 0.0) { double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1359,7 +1370,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_xyz.has_value()) { + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; From a5a4fc4dcfb3e5f4ea267e137e70e49b89499d1f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 12:54:22 +0200 Subject: [PATCH 163/327] Measuring - Gizmo measure shows arrows at endpoints of angle dimensioning --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8a4cd17d94..f55874071d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -929,13 +929,30 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.init_from(std::move(init_data)); } - // render arc + // arc const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); m_dimensioning.arc.render(); - // render edge 1 extension + // arrows + auto render_arrow = [this, shader, &camera, &normal, ¢er, &e1_unit, draw_radius, step, resolution](unsigned int endpoint_id) { + const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); + const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); + const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), direction_model) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal) * + Geometry::scale_transform(camera.get_inv_zoom()); + shader->set_uniform("view_model_matrix", view_model_matrix); + m_dimensioning.triangle.render(); + }; + + glsafe(::glDisable(GL_CULL_FACE)); + render_arrow(1); + render_arrow(2); + glsafe(::glEnable(GL_CULL_FACE)); + + // edge 1 extension const Vec3d e11e12 = e1.second - e1.first; const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); @@ -946,7 +963,7 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.line.render(); } - // render edge 2 extension + // edge 2 extension const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { @@ -956,12 +973,13 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.line.render(); } - // world coordinates - const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + // label + // label model coordinates + const Vec3d label_position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); - // screen coordinates + // label screen coordinates const std::array& viewport = camera.get_viewport(); - const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_world, m_volume_matrix, + const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_model, m_volume_matrix, camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); @@ -969,8 +987,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; - m_imgui->text(txt); + m_imgui->text(format_double(Geometry::rad2deg(angle)) + "°"); m_imgui->end(); ImGui::PopStyleVar(); }; From 2678ccef40ca6f50daf41e77d755e50cee782688 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 13:53:14 +0200 Subject: [PATCH 164/327] Follow-up of a5a4fc4dcfb3e5f4ea267e137e70e49b89499d1f - Fixed arrows orientations --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f55874071d..ff59ba0a5c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -940,9 +940,10 @@ void GLGizmoMeasure::render_dimensioning() const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const auto qz = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const auto qx = Eigen::Quaternion::FromTwoVectors(qz * Vec3d::UnitX(), direction_model); const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), direction_model) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal) * - Geometry::scale_transform(camera.get_inv_zoom()); + qx * qz * Geometry::scale_transform(camera.get_inv_zoom()); shader->set_uniform("view_model_matrix", view_model_matrix); m_dimensioning.triangle.render(); }; From ccfce4db7b039a38e6ceca168f503b7142ee8cfd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 13:59:11 +0200 Subject: [PATCH 165/327] Fixed warnings --- src/libslic3r/Measure.cpp | 21 ++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 529c6f1231..cce63e6465 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -568,7 +568,6 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, // Calculate edges on planes const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); - const double edges_angle = std::acos(std::clamp(edge_on_plane1_unit.dot(edge_on_plane2_unit), -1.0, 1.0)); const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; @@ -645,16 +644,16 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f2.get_type() == SurfaceFeatureType::Edge) { std::vector distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); - double distance = res.distance_strict->dist; - Vec3d v2 = res.distance_strict->to; - - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(distance, v, v2); - }; +// auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { +// const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); +// double distance = res.distance_strict->dist; +// Vec3d v2 = res.distance_strict->to; +// +// const Vec3d e1e2 = e.second - e.first; +// const Vec3d e1v2 = v2 - e.first; +// if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) +// distances.emplace_back(distance, v, v2); +// }; std::pair e1 = f1.get_edge(); std::pair e2 = f2.get_edge(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index ff59ba0a5c..13415877f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -993,7 +993,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PopStyleVar(); }; - auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_edge_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const std::pair e1 = res.angle->e1; @@ -1006,7 +1006,7 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_plane_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const std::pair e1 = res.angle->e1; From 58c7d8b1881127cafd971fa14dcf2fcc9ef9c5eb Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 3 Oct 2022 17:18:58 +0200 Subject: [PATCH 166/327] 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 09766bbac9..43624964a8 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 71a9c5dcf7..775cd32ccd 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 4335da35ae..355c9230e4 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 aa8cdda043..0fdc3b2db7 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 ed48b6e5e2..136276803e 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 3946e54e13..add1eb94c8 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 99e239301ae7078793a0dfc3c71f025cd02fbd95 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 08:17:43 +0200 Subject: [PATCH 167/327] Measuring - Fixes in plane-plane measurement - Measurements validation - Fixes in dimensioning rendering --- src/libslic3r/Measure.cpp | 20 +++++++++++--------- src/libslic3r/Measure.hpp | 4 ++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 20 ++++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index cce63e6465..2905f82553 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -719,21 +719,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto [idx1, normal1, pt1] = f1.get_plane(); const auto [idx2, normal2, pt2] = f2.get_plane(); - double angle = 0.; if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. - Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); - } else { - // Planes are not parallel, calculate angle. - angle = std::acos(std::abs(normal1.dot(normal2))); + const Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO } - result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + else + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); } - + // validation + if (result.distance_infinite.has_value() && result.distance_infinite->dist < EPSILON) + result.distance_infinite.reset(); + if (result.distance_strict.has_value() && result.distance_strict->dist < EPSILON) + result.distance_strict.reset(); + if (result.angle.has_value() && std::abs(result.angle->angle) < EPSILON) + result.angle.reset(); return result; } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index c59c9bbbc7..df148da761 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -136,6 +136,10 @@ struct MeasurementResult { std::optional distance_strict; std::optional distance_xyz; + bool has_distance_data() const { + return distance_infinite.has_value() || distance_strict.has_value(); + } + bool has_any_data() const { return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 13415877f9..5c467e0203 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1062,11 +1062,14 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); if (m_selected_features.second.feature.has_value()) { - // Render the arrow between the points that the backend passed: - const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() - ? *m_measurement_result.distance_infinite - : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to, dap.dist); + const bool has_distance = m_measurement_result.has_distance_data(); + if (has_distance) { + // Render the arrow between the points that the backend passed: + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to, dap.dist); + } const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); @@ -1080,7 +1083,7 @@ void GLGizmoMeasure::render_dimensioning() } // Where needed, draw also the extension of the edge to where the dist is measured: - if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + if (has_distance && ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) point_edge(*f1, *f2); // Now if there is an angle to show, draw the arc: @@ -1370,7 +1373,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_infinite.has_value() && measure.distance_infinite->dist > 0.0) { + if (measure.distance_infinite.has_value()) { double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1379,7 +1382,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_strict.has_value() && measure.distance_strict->dist > 0.0) { + if (measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; From cba2113bbbab540c55574edef1372086338b8836 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 08:45:39 +0200 Subject: [PATCH 168/327] Measuring - Refactoring in GLGizmoMeasure imgui dialog related to units --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5c467e0203..84b4fc0a97 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1232,7 +1232,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); + const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); if (m_curr_feature.has_value()) { const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); @@ -1263,7 +1263,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -1276,7 +1276,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit radius = ObjectManipulation::mm_to_in * radius; } add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -1369,7 +1369,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1378,7 +1378,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); - add_measure_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1388,7 +1388,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); - add_measure_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1397,7 +1397,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)4); - add_measure_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } From 8fd8bada373da8c823dbe2b47811b465856fe617 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 11:26:11 +0200 Subject: [PATCH 169/327] Measuring - Gizmo measure shows dimensioning for distance plane-circle --- src/libslic3r/Measure.cpp | 58 +++++++++++++++++------- src/libslic3r/Measure.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 2905f82553..852e9268cc 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -47,6 +47,7 @@ public: std::vector get_all_features() const; std::optional get_feature(size_t face_idx, const Vec3d& point) const; std::vector> get_planes_triangle_indices() const; + const std::vector& get_plane_features(unsigned int plane_id) const; private: void update_planes(); @@ -398,6 +399,11 @@ std::vector> MeasuringImpl::get_planes_triangle_indices() const return out; } +const std::vector& MeasuringImpl::get_plane_features(unsigned int plane_id) const +{ + assert(plane_id < m_planes.size()); + return m_planes[plane_id].surface_features; +} @@ -435,6 +441,10 @@ std::vector> Measuring::get_planes_triangle_indices() const return priv->get_planes_triangle_indices(); } +const std::vector& Measuring::get_plane_features(unsigned int plane_id) const +{ + return priv->get_plane_features(plane_id); +} const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; @@ -583,7 +593,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring) { assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); @@ -604,25 +614,25 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { - const auto& [s,e] = f2.get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - double dist_inf = line.distance(f1.get_point()); - Vec3d proj = line.projection(f1.get_point()); - double len_sq = (e-s).squaredNorm(); - double dist_start_sq = (proj-s).squaredNorm(); - double dist_end_sq = (proj-e).squaredNorm(); + const auto [s,e] = f2.get_edge(); + const Eigen::ParametrizedLine line(s, (e-s).normalized()); + const double dist_inf = line.distance(f1.get_point()); + const Vec3d proj = line.projection(f1.get_point()); + const double len_sq = (e-s).squaredNorm(); + const double dist_start_sq = (proj-s).squaredNorm(); + const double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints - bool s_is_closer = dist_start_sq < dist_end_sq; - result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); + const bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + sqr(dist_inf)), f1.get_point(), s_is_closer ? s : e}); } result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. - const auto& [c, radius, n] = f2.get_circle(); + const auto [c, radius, n] = f2.get_circle(); Eigen::Hyperplane circle_plane(n, c); Vec3d proj = circle_plane.projection(f1.get_point()); double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + @@ -632,7 +642,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - const auto& [idx, normal, pt] = f2.get_plane(); + const auto [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = @@ -678,7 +688,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const std::pair e = f1.get_edge(); const auto& [center, radius, normal] = f2.get_circle(); const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d e1e2_unit = e1e2.normalized(); std::vector distances; distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); @@ -709,14 +719,30 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + assert(measuring != nullptr); + + const auto [center, radius, normal1] = f1.get_circle(); + const auto [idx2, normal2, origin2] = f2.get_plane(); + + const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane(normal1, center).absDistance(origin2) < EPSILON; + if (!coplanar) { + const std::vector& plane_features = measuring->get_plane_features(idx2); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) + distances.push_back(*get_measurement(sf, f1).distance_infinite); + } + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Plane) { - assert(f2.get_type() == SurfaceFeatureType::Plane); - const auto [idx1, normal1, pt1] = f1.get_plane(); const auto [idx2, normal2, pt2] = f2.get_plane(); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index df148da761..2e17eea5c5 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -105,6 +105,9 @@ public: // call too often. std::vector> get_planes_triangle_indices() const; + // Returns the surface features of the plane with the given index + const std::vector& get_plane_features(unsigned int plane_id) const; + private: std::unique_ptr priv; }; @@ -146,7 +149,7 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr); inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 84b4fc0a97..31ce08fa74 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -175,7 +175,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) - m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get()); return true; } From 836b45a2ed01ce07061685911efe3831d926cf32 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 12:36:38 +0200 Subject: [PATCH 170/327] Measuring - Gizmo measure shows dimensioning for distance edge-plane --- src/libslic3r/Measure.cpp | 63 +++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 39 ++++++++------- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 852e9268cc..ea68503254 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -708,7 +708,45 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + assert(measuring != nullptr); + + const auto [from, to] = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + const Vec3d edge_unit = (to - from).normalized(); + if (are_perpendicular(edge_unit, normal)) { + std::vector distances; + const Eigen::Hyperplane plane(normal, origin); + distances.push_back(DistAndPoints{ plane.absDistance(from), from, plane.projection(from) }); + distances.push_back(DistAndPoints{ plane.absDistance(to), to, plane.projection(to) }); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + else { + const std::vector& plane_features = measuring->get_plane_features(idx); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + } result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); } /////////////////////////////////////////////////////////////////////////// @@ -729,14 +767,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const std::vector& plane_features = measuring->get_plane_features(idx2); std::vector distances; for (const SurfaceFeature& sf : plane_features) { - if (sf.get_type() == SurfaceFeatureType::Edge) - distances.push_back(*get_measurement(sf, f1).distance_infinite); + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } - auto it = std::min_element(distances.begin(), distances.end(), - [](const DistAndPoints& item1, const DistAndPoints& item2) { - return item1.dist < item2.dist; - }); - result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 31ce08fa74..051a7ee40a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -891,13 +891,15 @@ void GLGizmoMeasure::render_dimensioning() auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const double angle = res.angle->angle; - const Vec3d center = res.angle->center; - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; - const bool coplanar = res.angle->coplanar; + if (!m_measurement_result.angle.has_value()) + return; + + const double angle = m_measurement_result.angle->angle; + const Vec3d center = m_measurement_result.angle->center; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + const bool coplanar = m_measurement_result.angle->coplanar; if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) return; @@ -993,12 +995,15 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PopStyleVar(); }; - auto arc_edge_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; + if (!m_measurement_result.angle.has_value()) + return; + + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + if (calc_radius == 0.0) return; @@ -1006,12 +1011,12 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - auto arc_plane_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + if (calc_radius == 0.0) return; From 19222137253a2ef453927820b15e3b646cfebc50 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Oct 2022 14:23:45 +0200 Subject: [PATCH 171/327] Measuring: prototype for uniformly scale a volume by editing the value of the shown distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 111 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 63 ++++++------- src/slic3r/GUI/ImGuiWrapper.hpp | 4 +- 4 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 051a7ee40a..3e83418ef4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -220,6 +220,7 @@ void GLGizmoMeasure::data_changed() m_last_plane_idx = -1; m_selected_features.reset(); m_selection_raycasters.clear(); + m_editing_distance = false; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -256,6 +257,7 @@ void GLGizmoMeasure::on_set_state() m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); + m_editing_distance = false; } else { m_mode = EMode::BasicSelection; @@ -840,18 +842,79 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string value_str = format_double(value); + const std::string units = use_inches ? _u8L("in") : _u8L("mm"); + const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + static double edit_value = 0.0; + const Vec2d label_position = 0.5 * (v1ss + v2ss); m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); m_imgui->set_next_window_bg_alpha(0.0f); + + if (!m_editing_distance) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + ImGui::AlignTextToFramePadding(); + m_imgui->text(value_str + " " + units); + ImGui::SameLine(); + if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { + m_editing_distance = true; + edit_value = value; + m_imgui->requires_extra_frame(); + } + m_imgui->end(); + ImGui::PopStyleVar(3); + } + + auto perform_scale = [this, value](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + + if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) + ImGui::OpenPopup("distance_popup"); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : - format_double(distance) + " " + _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); + if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + ImGui::PushItemWidth(value_str_width); + if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Scale"))) { + perform_scale(edit_value, value); + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Cancel"))) { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::PopStyleVar(4); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1194,7 +1257,10 @@ void GLGizmoMeasure::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("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (m_editing_distance) + return; + + m_imgui->begin(_u8L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar const float win_h = ImGui::GetWindowHeight(); @@ -1341,28 +1407,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text_colored(col_2_color, col_2); ImGui::TableSetColumnIndex(2); - ImGuiIO& io = ImGui::GetIO(); - const ImTextureID tex_id = io.Fonts->TexID; - assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); - float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); - float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); - const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); - const ImVec2 size = { float(rect->Width), float(rect->Height) }; - const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); - const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); - 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.25f, 0.25f, 0.25f, 1.0f }); - if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + if (m_imgui->image_button(ImGui::ClipboardBtnIcon, _L("Copy to clipboard"))) { wxTheClipboard->Open(); wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); wxTheClipboard->Close(); } - ImGui::PopStyleColor(3); - if (ImGui::IsItemHovered()) { - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); - } }; if (m_selected_features.second.feature.has_value()) { @@ -1373,7 +1422,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - ImGui::PushID((void*)(intptr_t)1); + ImGui::PushID("ClipboardAngle"); add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1382,7 +1431,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)2); + ImGui::PushID("ClipboardDistanceInfinite"); add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1392,7 +1441,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)3); + ImGui::PushID("ClipboardDistanceStrict"); add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1401,7 +1450,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)4); + ImGui::PushID("ClipboardDistanceXYZ"); add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index a5312ebf57..bba23fb6b8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -118,6 +118,7 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; SelectedFeatures m_selected_features; + bool m_editing_distance{ false }; void update_if_needed(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 4ce9dfefc0..74e125053e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -431,36 +431,6 @@ bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool a } #endif // ENABLE_PREVIEW_LAYOUT -bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) -{ - return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); -} - -bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format) -{ - auto label_utf8 = into_u8(label); - return input_double(label_utf8, value, format); -} - -bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format) -{ - bool value_changed = false; - - ImGui::BeginGroup(); - - for (int i = 0; i < 3; ++i) - { - std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z"); - ImGui::PushID(i); - ImGui::PushItemWidth(width); - value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast(&value(i)), 0.0f, 0.0f, format.c_str()); - ImGui::PopID(); - } - ImGui::EndGroup(); - - return value_changed; -} - bool ImGuiWrapper::checkbox(const wxString &label, bool &value) { auto label_utf8 = into_u8(label); @@ -519,20 +489,20 @@ void ImGuiWrapper::text_wrapped(const wxString &label, float wrap_width) void ImGuiWrapper::tooltip(const char *label, float wrap_width) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8.0f, 8.0f }); ImGui::BeginTooltip(); ImGui::PushTextWrapPos(wrap_width); ImGui::TextUnformatted(label); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); + ImGui::PopStyleVar(3); } void ImGuiWrapper::tooltip(const wxString &label, float wrap_width) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(wrap_width); - ImGui::TextUnformatted(label.ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + tooltip(label.ToUTF8().data(), wrap_width); } ImVec2 ImGuiWrapper::get_slider_icon_size() const @@ -684,6 +654,29 @@ bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags); } +bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip) +{ + const ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + 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.25f, 0.25f, 0.25f, 1.0f }); + const bool res = image_button(tex_id, size, uv0, uv1); + ImGui::PopStyleColor(3); + + if (!tooltip.empty() && ImGui::IsItemHovered()) + this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f); + + return res; +} + bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags) { // this is to force the label to the left of the widget: diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index f461bc970a..f75200b071 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -95,9 +95,6 @@ public: #if ENABLE_PREVIEW_LAYOUT bool draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback); #endif // ENABLE_PREVIEW_LAYOUT - bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); - bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); - bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool checkbox(const wxString &label, bool &value); void text(const char *label); void text(const std::string &label); @@ -118,6 +115,7 @@ public: bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); + bool image_button(const wchar_t icon, const wxString& tooltip = L""); // Use selection = -1 to not mark any option as selected bool combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0); From 8312dc24547df8e622bde814efb1b3e0b15bab40 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Oct 2022 14:47:25 +0200 Subject: [PATCH 172/327] Measuring: Fixed rendering of point features when the object is scaled --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 157 ++++++++++++----------- 1 file changed, 79 insertions(+), 78 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e83418ef4..a6efc09e78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -467,93 +467,94 @@ void GLGizmoMeasure::on_render() auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { - switch (feature.get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - } - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, normal] = feature.get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); - } - // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); - set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - } - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = feature.get_edge(); - // render extra point - const std::optional extra = feature.get_extra_point(); - if (extra.has_value()) { - const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(point_matrix); + const Transform3d model_matrix_scale_inverse = Geometry::Transformation(model_matrix).get_scaling_factor_matrix().inverse(); + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); if (update_raycasters) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(point_matrix); + it->second->set_transform(feature_matrix); } + break; } - // render edge - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(colors.back()); - m_cylinder.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = feature.get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = feature.get_edge(); + // render extra point + const std::optional extra = feature.get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + // render edge + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(colors.back()); + m_cylinder.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + } + break; } - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); - m_plane_models_cache[idx].set_color(colors.front()); - m_plane_models_cache[idx].render(); - if (update_raycasters) { - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); } - break; - } - } }; auto hover_selection_color = [this]() { From 1a0294e892476324936907eb8028a43c93c12b7b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 08:40:23 +0200 Subject: [PATCH 173/327] Measuring - Gizmo measure disabled for scaled volumes --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 903612f29b..1d13d92f10 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -78,6 +78,7 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) +#define DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES (1 && ENABLE_MEASURE_GIZMO) #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a6efc09e78..f2638d0588 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -290,8 +290,12 @@ bool GLGizmoMeasure::on_is_activable() const bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); - if (res) + if (res) { res &= !selection.get_first_volume()->is_sinking(); +#if DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES + res &= Geometry::Transformation(selection.get_first_volume()->world_matrix()).get_scaling_factor().isApprox(Vec3d::Ones()); +#endif // DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES + } return res; } From 5ebfa73f374265ec1f035ac7d1c66d587b990530 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 09:57:13 +0200 Subject: [PATCH 174/327] Measuring - Gizmo measure - Disable background fadeout animation when showing imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 7 +++++++ src/slic3r/GUI/ImGuiWrapper.hpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f2638d0588..d18584a870 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -903,6 +903,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + m_imgui->disable_background_fadeout_animation(); ImGui::PushItemWidth(value_str_width); if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 74e125053e..7484724097 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1145,6 +1145,13 @@ ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) } #endif // ENABLE_LEGEND_TOOLBAR_ICONS +#if ENABLE_MEASURE_GIZMO +void ImGuiWrapper::disable_background_fadeout_animation() +{ + GImGui->DimBgRatio = 1.0f; +} +#endif // ENABLE_MEASURE_GIZMO + ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) { return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() }); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index f75200b071..bf4018fb15 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -136,6 +136,10 @@ public: void set_requires_extra_frame() { m_requires_extra_frame = true; } void reset_requires_extra_frame() { m_requires_extra_frame = false; } +#if ENABLE_MEASURE_GIZMO + void disable_background_fadeout_animation(); +#endif // ENABLE_MEASURE_GIZMO + static ImU32 to_ImU32(const ColorRGBA& color); static ImVec4 to_ImVec4(const ColorRGBA& color); static ColorRGBA from_ImU32(const ImU32& color); From 0fd01f36bdf3f3a6bd0b4a564f06b10e5cce9367 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 10:42:01 +0200 Subject: [PATCH 175/327] Measuring - Gizmo measure - Handle [Enter] and [Esc] keys input events in imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 78 +++++++++++++----------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d18584a870..3e1d9824d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -848,10 +848,10 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; - const std::string value_str = format_double(value); + const double curr_value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string curr_value_str = format_double(curr_value); const std::string units = use_inches ? _u8L("in") : _u8L("mm"); - const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + const float value_str_width = 20.0f + ImGui::CalcTextSize(curr_value_str.c_str()).x; static double edit_value = 0.0; const Vec2d label_position = 0.5 * (v1ss + v2ss); @@ -864,37 +864,17 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); ImGui::AlignTextToFramePadding(); - m_imgui->text(value_str + " " + units); + m_imgui->text(curr_value_str + " " + units); ImGui::SameLine(); if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { m_editing_distance = true; - edit_value = value; + edit_value = curr_value; m_imgui->requires_extra_frame(); } m_imgui->end(); ImGui::PopStyleVar(3); } - auto perform_scale = [this, value](double new_value, double old_value) { - if (new_value == old_value || new_value <= 0.0) - return; - - const double ratio = new_value / old_value; - wxGetApp().plater()->take_snapshot(_L("Scale")); - - TransformationType type; - type.set_world(); - type.set_relative(); - type.set_joint(); - - // apply scale - Selection& selection = m_parent.get_selection(); - selection.setup_cache(); - selection.scale(ratio * Vec3d::Ones(), type); - wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot - wxGetApp().obj_manipul()->set_dirty(); - }; - if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) ImGui::OpenPopup("distance_popup"); @@ -903,21 +883,51 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + auto perform_scale = [this](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + auto action_exit = [this]() { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + }; + auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { + perform_scale(new_value, old_value); + action_exit(); + }; + m_imgui->disable_background_fadeout_animation(); ImGui::PushItemWidth(value_str_width); if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } + + // handle keys input + if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + action_scale(edit_value, curr_value); + else if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + action_exit(); + ImGui::SameLine(); - if (m_imgui->button(_u8L("Scale"))) { - perform_scale(edit_value, value); - m_editing_distance = false; - ImGui::CloseCurrentPopup(); - } + if (m_imgui->button(_u8L("Scale"))) + action_scale(edit_value, curr_value); ImGui::SameLine(); - if (m_imgui->button(_u8L("Cancel"))) { - m_editing_distance = false; - ImGui::CloseCurrentPopup(); - } + if (m_imgui->button(_u8L("Cancel"))) + action_exit(); ImGui::EndPopup(); } ImGui::PopStyleVar(4); From 74a32e3261f672e67457ec06944dddb27cf8f4b8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Oct 2022 10:03:55 +0200 Subject: [PATCH 176/327] 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 01cd374143..0d54ebc1f7 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 775cd32ccd..c90211d427 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 355c9230e4..c3916e4a8f 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 2fb59e66c2b6f07a25cfa873fdeb1d820b2d78c4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 12:42:30 +0200 Subject: [PATCH 177/327] easuring - Gizmo measure - Reworked imgui dialog layout to avoid change in size in dependence of hovered or selected features --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 266 +++++++++++++---------- 1 file changed, 151 insertions(+), 115 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e1d9824d4..15d6ea761f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -63,7 +63,7 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty switch (type) { default: - case Measure::SurfaceFeatureType::Undef: { return _u8L("Undefined"); } + case Measure::SurfaceFeatureType::Undef: { return _u8L("No feature"); } case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } @@ -1292,6 +1292,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } if (ImGui::BeginTable("Commands", 2)) { + unsigned int row_count = 1; add_row_to_table( [this]() { m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); @@ -1310,93 +1311,121 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); - if (m_selected_features.first.feature.has_value()) + if (m_selected_features.first.feature.has_value()) { add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } - if (m_mode == EMode::BasicSelection && m_hover_id != -1) + if (m_mode == EMode::BasicSelection && m_hover_id != -1) { add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = row_count; i < 3; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); } const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); - if (m_curr_feature.has_value()) { - const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); + const Measure::SurfaceFeatureType feature_type = m_curr_feature.has_value() ? m_curr_feature->get_type() : Measure::SurfaceFeatureType::Undef; + bool data_text_set = false; + ImGui::Separator(); + if (feature_type != Measure::SurfaceFeatureType::Undef) { if (m_mode == EMode::BasicSelection) { - if (feature_type != Measure::SurfaceFeatureType::Undef) { - ImGui::Separator(); - m_imgui->text(surface_feature_type_as_string(feature_type) + ":"); - if (ImGui::BeginTable("Data", 2)) { - switch (feature_type) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - Vec3d position = m_volume_matrix * m_curr_feature->get_point(); - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = m_volume_matrix * from; - to = m_volume_matrix * to; - if (use_inches) { - from = ObjectManipulation::mm_to_in * from; - to = ObjectManipulation::mm_to_in * to; - } - add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = m_volume_matrix * center; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) { - center = ObjectManipulation::mm_to_in * center; - radius = ObjectManipulation::mm_to_in * radius; - } - add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = m_volume_matrix * origin; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) - origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - } - ImGui::EndTable(); - } - } + m_imgui->text(surface_feature_type_as_string(feature_type)); + data_text_set = true; } else if (m_mode == EMode::ExtendedSelection) { if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { - ImGui::Separator(); - m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); - if (ImGui::BeginTable("Data", 2)) { - Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::EndTable(); - } + m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); + data_text_set = true; } } } + if (!data_text_set) + m_imgui->text(_u8L("No feature")); + + const unsigned int max_data_row_count = 3; + unsigned int data_row_count = 0; + if (ImGui::BeginTable("Data", 2)) { + if (m_mode == EMode::BasicSelection) { + switch (feature_type) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + Vec3d position = m_volume_matrix * m_curr_feature->get_point(); + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 1; + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = m_volume_matrix * from; + to = m_volume_matrix * to; + if (use_inches) { + from = ObjectManipulation::mm_to_in * from; + to = ObjectManipulation::mm_to_in * to; + } + add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 3; + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = m_volume_matrix * center; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) { + center = ObjectManipulation::mm_to_in * center; + radius = ObjectManipulation::mm_to_in * radius; + } + add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 3; + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) + origin = ObjectManipulation::mm_to_in * origin; + add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 2; + break; + } + } + } + else { + if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { + Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 1; + } + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = data_row_count; i < max_data_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; @@ -1430,52 +1459,59 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } }; - if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); + m_imgui->text(_u8L("Measure")); - ImGui::Separator(); - if (measure.has_any_data()) { - m_imgui->text(_u8L("Measure") + ":"); - if (ImGui::BeginTable("Measure", 3)) { - if (measure.angle.has_value()) { - ImGui::PushID("ClipboardAngle"); - add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_infinite.has_value()) { - double distance = measure.distance_infinite->dist; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceInfinite"); - add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_strict.has_value() && - (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { - double distance = measure.distance_strict->dist; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceStrict"); - add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { - Vec3d distance = *measure.distance_xyz; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceXYZ"); - add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - ImGui::EndTable(); + const unsigned int max_measure_row_count = 2; + unsigned int measure_row_count = 0; + if (ImGui::BeginTable("Measure", 4)) { + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult& measure = m_measurement_result; + if (measure.angle.has_value()) { + ImGui::PushID("ClipboardAngle"); + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_infinite.has_value()) { + double distance = measure.distance_infinite->dist; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceInfinite"); + add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { + double distance = measure.distance_strict->dist; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceStrict"); + add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { + Vec3d distance = *measure.distance_xyz; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceXYZ"); + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); } } - else - m_imgui->text(_u8L("No measure available")); + + // add dummy rows to keep dialog size fixed + for (unsigned int i = measure_row_count; i < max_measure_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); } if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { From 85af9b93f1e886ea0f80d890d97ac967da772cb9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 7 Oct 2022 16:26:19 +0200 Subject: [PATCH 178/327] 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 270ccb1bea..597152c06a 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 452f620c47..f907b03e1e 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 3cfc55adc8..5164c691e2 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 bbbc00d42b..7190edb64c 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 d3f1d1bf27..cced147f8e 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 cb498f87ef..3dd02ab8dc 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 c90211d427..d751325424 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 c3916e4a8f..21d7c64f8a 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 0475fe395b..e2fca5faed 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 12a654175b..7c7fbc9898 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 f0925e74f720a5f8da61f63c08190294b2cb2c97 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 08:29:51 +0200 Subject: [PATCH 179/327] Follow-up of 2fb59e66c2b6f07a25cfa873fdeb1d820b2d78c4 - Removed obsolete assert --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 15d6ea761f..79a800bf2e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1356,7 +1356,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (m_mode == EMode::BasicSelection) { switch (feature_type) { - default: { assert(false); break; } + default: { break; } case Measure::SurfaceFeatureType::Point: { Vec3d position = m_volume_matrix * m_curr_feature->get_point(); From 614b1581a662952d5d282427e6fd353b007d610f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 09:46:12 +0200 Subject: [PATCH 180/327] Measuring - Gizmo measure - Auto-select text when opening imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 10 ++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 11 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 79a800bf2e..b4c35b1563 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -221,6 +221,7 @@ void GLGizmoMeasure::data_changed() m_selected_features.reset(); m_selection_raycasters.clear(); m_editing_distance = false; + m_is_editing_distance_first_frame = true; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -258,6 +259,7 @@ void GLGizmoMeasure::on_set_state() m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); m_editing_distance = false; + m_is_editing_distance_first_frame = true; } else { m_mode = EMode::BasicSelection; @@ -904,6 +906,7 @@ void GLGizmoMeasure::render_dimensioning() }; auto action_exit = [this]() { m_editing_distance = false; + m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { @@ -916,6 +919,13 @@ void GLGizmoMeasure::render_dimensioning() if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } + // trick to auto-select text in the input widgets on 1st frame + if (m_is_editing_distance_first_frame) { + ImGui::SetKeyboardFocusHere(0); + m_is_editing_distance_first_frame = false; + m_imgui->set_requires_extra_frame(); + } + // handle keys input if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) action_scale(edit_value, curr_value); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index bba23fb6b8..9db2a73aa3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -119,6 +119,7 @@ class GLGizmoMeasure : public GLGizmoBase SelectedFeatures m_selected_features; bool m_editing_distance{ false }; + bool m_is_editing_distance_first_frame{ true }; void update_if_needed(); From 6cee261f3969f0070ade5985940d92fad44cdd89 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 10:12:14 +0200 Subject: [PATCH 181/327] Follow-up of 8312dc24547df8e622bde814efb1b3e0b15bab40 - Fixed rendering of point on locked features when the object is scaled --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 34 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b4c35b1563..1976cbcdab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -317,7 +317,6 @@ void GLGizmoMeasure::on_render() (selection.is_single_volume() || selection.is_single_volume_instance())) { update_if_needed(); - m_volume_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); @@ -397,7 +396,6 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); - assert(res); if (res) { if (callback) p = callback(p); @@ -472,15 +470,14 @@ void GLGizmoMeasure::on_render() }; auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { - const Transform3d model_matrix_scale_inverse = Geometry::Transformation(model_matrix).get_scaling_factor_matrix().inverse(); + float inv_zoom, bool update_raycasters) { switch (feature.get_type()) { default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d feature_matrix = m_volume_matrix * Geometry::translation_transform(position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -495,7 +492,7 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = feature.get_circle(); // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d center_matrix = m_volume_matrix * Geometry::translation_transform(center) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -505,7 +502,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); m_circle.model.set_color(colors.back()); m_circle.model.render(); @@ -522,7 +519,7 @@ void GLGizmoMeasure::on_render() // render extra point const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { - const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d point_matrix = m_volume_matrix * Geometry::translation_transform(*extra) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -533,16 +530,16 @@ void GLGizmoMeasure::on_render() } } // render edge - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + const Transform3d edge_matrix = m_volume_matrix * Geometry::translation_transform(start) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); + set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); if (update_raycasters) { auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); + it->second->set_transform(edge_matrix); } break; } @@ -550,13 +547,13 @@ void GLGizmoMeasure::on_render() { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); + set_matrix_uniforms(m_volume_matrix); m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].render(); if (update_raycasters) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); + it->second->set_transform(m_volume_matrix); } break; } @@ -601,13 +598,13 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, m_volume_matrix, inv_zoom, true); + render_feature(*m_curr_feature, colors, inv_zoom, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); - render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); @@ -618,7 +615,7 @@ void GLGizmoMeasure::on_render() if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); - render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); @@ -629,7 +626,7 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -675,6 +672,9 @@ void GLGizmoMeasure::update_if_needed() } m_old_model_object = object; m_old_model_volume = volume; + + m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + m_volume_matrix_scale_inverse = Geometry::Transformation(m_volume_matrix).get_scaling_factor_matrix().inverse(); }; const ModelObject* mo = m_c->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9db2a73aa3..fd93ba4aac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,6 +87,7 @@ class GLGizmoMeasure : public GLGizmoBase Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; + Transform3d m_volume_matrix_scale_inverse{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::vector> m_selection_raycasters; From 2972493f5fb66a418c61ca5c3d9f16901187a0b1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 10:47:20 +0200 Subject: [PATCH 182/327] Measuring: Gizmo measure shows dimensioning for distance circle-circle --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Measure.cpp | 241 +++++++++++++++++++- src/libslic3r/Measure.hpp | 4 + src/libslic3r/MeasureUtils.hpp | 390 +++++++++++++++++++++++++++++++++ 4 files changed, 632 insertions(+), 4 deletions(-) create mode 100644 src/libslic3r/MeasureUtils.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index cdf61407f2..e91a2186c0 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -182,6 +182,7 @@ set(SLIC3R_SOURCES MeshNormals.cpp Measure.hpp Measure.cpp + MeasureUtils.hpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index ea68503254..27a4f8a90d 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1,10 +1,11 @@ #include "libslic3r/libslic3r.h" #include "Measure.hpp" +#include "MeasureUtils.hpp" #include "libslic3r/Geometry/Circle.hpp" #include "libslic3r/SurfaceMesh.hpp" - +#if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace Measure { @@ -13,8 +14,6 @@ namespace Measure { constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ... - - static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) { Vec2ds pts; @@ -754,7 +753,238 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + const auto [c0, r0, n0] = f1.get_circle(); + const auto [c1, r1, n1] = f2.get_circle(); + + // The following code is an adaptation of the algorithm found in: + // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h + // and described in: + // https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf + + struct ClosestInfo + { + double sqrDistance{ 0.0 }; + Vec3d circle0Closest{ Vec3d::Zero() }; + Vec3d circle1Closest{ Vec3d::Zero() }; + + inline bool operator < (const ClosestInfo& other) const { return sqrDistance < other.sqrDistance; } + }; + std::array candidates{}; + + const double zero = 0.0; + + const Vec3d D = c1 - c0; + + if (!are_parallel(n0, n1)) { + auto orthonormal_basis = [](const Vec3d& v) { + std::array ret; + ret[2] = v.normalized(); + int index; + ret[2].maxCoeff(&index); + switch (index) + { + case 0: { ret[0] = Vec3d(ret[2].y(), -ret[2].x(), 0.0).normalized(); break; } + case 1: { ret[0] = Vec3d(0.0, ret[2].z(), -ret[2].y()).normalized(); break; } + case 2: { ret[0] = Vec3d(-ret[2].z(), 0.0, ret[2].x()).normalized(); break; } + } + ret[1] = ret[2].cross(ret[0]).normalized(); + return ret; + }; + + // Get parameters for constructing the degree-8 polynomial phi. + const double one = 1.0; + const double two = 2.0; + const double r0sqr = sqr(r0); + const double r1sqr = sqr(r1); + + // Compute U1 and V1 for the plane of circle1. + const std::array basis = orthonormal_basis(n1); + const Vec3d U1 = basis[0]; + const Vec3d V1 = basis[1]; + + // Construct the polynomial phi(cos(theta)). + const Vec3d N0xD = n0.cross(D); + const Vec3d N0xU1 = n0.cross(U1); + const Vec3d N0xV1 = n0.cross(V1); + const double a0 = r1 * D.dot(U1); + const double a1 = r1 * D.dot(V1); + const double a2 = N0xD.dot(N0xD); + const double a3 = r1 * N0xD.dot(N0xU1); + const double a4 = r1 * N0xD.dot(N0xV1); + const double a5 = r1sqr * N0xU1.dot(N0xU1); + const double a6 = r1sqr * N0xU1.dot(N0xV1); + const double a7 = r1sqr * N0xV1.dot(N0xV1); + Polynomial1 p0{ a2 + a7, two * a3, a5 - a7 }; + Polynomial1 p1{ two * a4, two * a6 }; + Polynomial1 p2{ zero, a1 }; + Polynomial1 p3{ -a0 }; + Polynomial1 p4{ -a6, a4, two * a6 }; + Polynomial1 p5{ -a3, a7 - a5 }; + Polynomial1 tmp0{ one, zero, -one }; + Polynomial1 tmp1 = p2 * p2 + tmp0 * p3 * p3; + Polynomial1 tmp2 = two * p2 * p3; + Polynomial1 tmp3 = p4 * p4 + tmp0 * p5 * p5; + Polynomial1 tmp4 = two * p4 * p5; + Polynomial1 p6 = p0 * tmp1 + tmp0 * p1 * tmp2 - r0sqr * tmp3; + Polynomial1 p7 = p0 * tmp2 + p1 * tmp1 - r0sqr * tmp4; + + // Parameters for polynomial root finding. The roots[] array + // stores the roots. We need only the unique ones, which is + // the responsibility of the set uniqueRoots. The pairs[] + // array stores the (cosine,sine) information mentioned in the + // PDF. TODO: Choose the maximum number of iterations for root + // finding based on specific polynomial data? + const uint32_t maxIterations = 128; + int32_t degree = 0; + size_t numRoots = 0; + std::array roots{}; + std::set uniqueRoots{}; + size_t numPairs = 0; + std::array, 16> pairs{}; + double temp = zero; + double sn = zero; + + if (p7.GetDegree() > 0 || p7[0] != zero) { + // H(cs,sn) = p6(cs) + sn * p7(cs) + Polynomial1 phi = p6 * p6 - tmp0 * p7 * p7; + degree = static_cast(phi.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &phi[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = p7(cs); + if (temp != zero) { + sn = -p6(cs) / temp; + pairs[numPairs++] = std::make_pair(cs, sn); + } + else { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + } + else { + // H(cs,sn) = p6(cs) + degree = static_cast(p6.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &p6[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + + for (size_t i = 0; i < numPairs; ++i) { + ClosestInfo& info = candidates[i]; + Vec3d delta = D + r1 * (pairs[i].first * U1 + pairs[i].second * V1); + info.circle1Closest = c0 + delta; + const double N0dDelta = n0.dot(delta); + const double lenN0xDelta = n0.cross(delta).norm(); + if (lenN0xDelta > 0.0) { + const double diff = lenN0xDelta - r0; + info.sqrDistance = sqr(N0dDelta) + sqr(diff); + delta -= N0dDelta * n0; + delta.normalize(); + info.circle0Closest = c0 + r0 * delta; + } + else { + const Vec3d r0U0 = r0 * get_orthogonal(n0, true); + const Vec3d diff = delta - r0U0; + info.sqrDistance = diff.dot(diff); + info.circle0Closest = c0 + r0U0; + } + } + + std::sort(candidates.begin(), candidates.begin() + numPairs); + } + else { + ClosestInfo& info = candidates[0]; + + const double N0dD = n0.dot(D); + const Vec3d normProj = N0dD * n0; + const Vec3d compProj = D - normProj; + Vec3d U = compProj; + const double d = U.norm(); + U.normalize(); + + // The configuration is determined by the relative location of the + // intervals of projection of the circles on to the D-line. + // Circle0 projects to [-r0,r0] and circle1 projects to + // [d-r1,d+r1]. + const double dmr1 = d - r1; + double distance; + if (dmr1 >= r0) { + // d >= r0 + r1 + // The circles are separated (d > r0 + r1) or tangent with one + // outside the other (d = r0 + r1). + distance = dmr1 - r0; + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // d < r0 + r1 + // The cases implicitly use the knowledge that d >= 0. + const double dpr1 = d + r1; + if (dpr1 <= r0) { + // Circle1 is inside circle0. + distance = r0 - dpr1; + if (d > 0.0) { + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else if (dmr1 <= -r0) { + // Circle0 is inside circle1. + distance = -r0 - dmr1; + if (d > 0.0) { + info.circle0Closest = c0 - r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else { + distance = (c1 - c0).norm(); + info.circle0Closest = c0; + info.circle1Closest = c1; + } + } + + info.sqrDistance = distance * distance + N0dD * N0dD; + } + + result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { assert(measuring != nullptr); @@ -823,3 +1053,6 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } // namespace Measure } // namespace Slic3r + + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 2e17eea5c5..f310d8f9fa 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,8 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#if ENABLE_MEASURE_GIZMO + #include #include @@ -191,3 +193,5 @@ inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2 } // namespace Slic3r #endif // Slic3r_Measure_hpp_ + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp new file mode 100644 index 0000000000..4f84624eac --- /dev/null +++ b/src/libslic3r/MeasureUtils.hpp @@ -0,0 +1,390 @@ +#ifndef Slic3r_MeasureUtils_hpp_ +#define Slic3r_MeasureUtils_hpp_ + +#if ENABLE_MEASURE_GIZMO + +#include + +namespace Slic3r { +namespace Measure { + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h + +class Polynomial1 +{ +public: + Polynomial1(std::initializer_list values) + { + // C++ 11 will call the default constructor for + // Polynomial1 p{}, so it is guaranteed that + // values.size() > 0. + m_coefficient.resize(values.size()); + std::copy(values.begin(), values.end(), m_coefficient.begin()); + EliminateLeadingZeros(); + } + + // Construction and destruction. The first constructor creates a + // polynomial of the specified degree but sets all coefficients to + // zero (to ensure initialization). You are responsible for setting + // the coefficients, presumably with the degree-term set to a nonzero + // number. In the second constructor, the degree is the number of + // initializers plus 1, but then adjusted so that coefficient[degree] + // is not zero (unless all initializer values are zero). + explicit Polynomial1(uint32_t degree) + : m_coefficient(static_cast(degree) + 1, 0.0) + {} + + // Eliminate any leading zeros in the polynomial, except in the case + // the degree is 0 and the coefficient is 0. The elimination is + // necessary when arithmetic operations cause a decrease in the degree + // of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) = + // (2 + 3*x). The inputs both have degree 2, so the result is created + // with degree 2. After the addition we find that the degree is in + // fact 1 and resize the array of coefficients. This function is + // called internally by the arithmetic operators, but it is exposed in + // the public interface in case you need it for your own purposes. + void EliminateLeadingZeros() + { + const size_t size = m_coefficient.size(); + if (size > 1) { + const double zero = 0.0; + int32_t leading; + for (leading = static_cast(size) - 1; leading > 0; --leading) { + if (m_coefficient[leading] != zero) + break; + } + + m_coefficient.resize(++leading); + } + } + + // Set all coefficients to the specified value. + void SetCoefficients(double value) + { + std::fill(m_coefficient.begin(), m_coefficient.end(), value); + } + + inline uint32_t GetDegree() const + { + // By design, m_coefficient.size() > 0. + return static_cast(m_coefficient.size() - 1); + } + + inline const double& operator[](uint32_t i) const { return m_coefficient[i]; } + inline double& operator[](uint32_t i) { return m_coefficient[i]; } + + // Evaluate the polynomial. If the polynomial is invalid, the + // function returns zero. + double operator()(double t) const + { + int32_t i = static_cast(m_coefficient.size()); + double result = m_coefficient[--i]; + for (--i; i >= 0; --i) { + result *= t; + result += m_coefficient[i]; + } + return result; + } + +protected: + // The class is designed so that m_coefficient.size() >= 1. + std::vector m_coefficient; +}; + +Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + Polynomial1 result(p0Degree + p1Degree); + result.SetCoefficients(0.0); + for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) { + for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) { + result[i0 + i1] += p0[i0] * p1[i1]; + } + } + return result; +} + +Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = -p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +Polynomial1 operator * (double scalar, const Polynomial1& p) +{ + const uint32_t degree = p.GetDegree(); + Polynomial1 result(degree); + for (uint32_t i = 0; i <= degree; ++i) { + result[i] = scalar * p[i]; + } + return result; +} + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h + +class RootsPolynomial +{ +public: + // General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c' + // must have at least d+1 elements and the output array 'root' must + // have at least d elements. + + // Find the roots on (-infinity,+infinity). + static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots) + { + if (degree >= 0 && c != nullptr) { + const double zero = 0.0; + while (degree >= 0 && c[degree] == zero) { + --degree; + } + + if (degree > 0) { + // Compute the Cauchy bound. + const double one = 1.0; + const double invLeading = one / c[degree]; + double maxValue = zero; + for (int32_t i = 0; i < degree; ++i) { + const double value = std::fabs(c[i] * invLeading); + if (value > maxValue) + maxValue = value; + } + const double bound = one + maxValue; + + return FindRecursive(degree, c, -bound, bound, maxIterations, roots); + } + else if (degree == 0) + // The polynomial is a nonzero constant. + return 0; + else { + // The polynomial is identically zero. + roots[0] = zero; + return 1; + } + } + else + // Invalid degree or c. + return 0; + } + + // If you know that p(tmin) * p(tmax) <= 0, then there must be at + // least one root in [tmin, tmax]. Compute it using bisection. + static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root) + { + const double zero = 0.0; + double pmin = Evaluate(degree, c, tmin); + if (pmin == zero) { + root = tmin; + return true; + } + double pmax = Evaluate(degree, c, tmax); + if (pmax == zero) { + root = tmax; + return true; + } + + if (pmin * pmax > zero) + // It is not known whether the interval bounds a root. + return false; + + if (tmin >= tmax) + // Invalid ordering of interval endpoitns. + return false; + + for (uint32_t i = 1; i <= maxIterations; ++i) { + root = 0.5 * (tmin + tmax); + + // This test is designed for 'float' or 'double' when tmin + // and tmax are consecutive floating-point numbers. + if (root == tmin || root == tmax) + break; + + const double p = Evaluate(degree, c, root); + const double product = p * pmin; + if (product < zero) { + tmax = root; + pmax = p; + } + else if (product > zero) { + tmin = root; + pmin = p; + } + else + break; + } + + return true; + } + + // Support for the Find functions. + static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots) + { + // The base of the recursion. + const double zero = 0.0; + double root = zero; + if (degree == 1) { + int32_t numRoots; + if (c[1] != zero) { + root = -c[0] / c[1]; + numRoots = 1; + } + else if (c[0] == zero) { + root = zero; + numRoots = 1; + } + else + numRoots = 0; + + if (numRoots > 0 && tmin <= root && root <= tmax) { + roots[0] = root; + return 1; + } + return 0; + } + + // Find the roots of the derivative polynomial scaled by 1/degree. + // The scaling avoids the factorial growth in the coefficients; + // for example, without the scaling, the high-order term x^d + // becomes (d!)*x through multiple differentiations. With the + // scaling we instead get x. This leads to better numerical + // behavior of the root finder. + const int32_t derivDegree = degree - 1; + std::vector derivCoeff(static_cast(derivDegree) + 1); + std::vector derivRoots(derivDegree); + for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) { + derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree; + } + const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]); + + int32_t numRoots = 0; + if (numDerivRoots > 0) { + // Find root on [tmin,derivRoots[0]]. + if (Find(degree, c, tmin, derivRoots[0], maxIterations, root)) + roots[numRoots++] = root; + + // Find root on [derivRoots[i],derivRoots[i+1]]. + for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) { + if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root)) + roots[numRoots++] = root; + } + + // Find root on [derivRoots[numDerivRoots-1],tmax]. + if (Find(degree, c, derivRoots[static_cast(numDerivRoots) - 1], tmax, maxIterations, root)) + roots[numRoots++] = root; + } + else { + // The polynomial is monotone on [tmin,tmax], so has at most one root. + if (Find(degree, c, tmin, tmax, maxIterations, root)) + roots[numRoots++] = root; + } + return numRoots; + } + + static double Evaluate(int32_t degree, const double* c, double t) + { + int32_t i = degree; + double result = c[i]; + while (--i >= 0) { + result = t * result + c[i]; + } + return result; + } +}; + +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h + +// Construct a single vector orthogonal to the nonzero input vector. If +// the maximum absolute component occurs at index i, then the orthogonal +// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components +// zero. The index addition i+1 is computed modulo N. +Vec3d get_orthogonal(const Vec3d& v, bool unitLength) +{ + double cmax = std::fabs(v[0]); + int32_t imax = 0; + for (int32_t i = 1; i < 3; ++i) { + double c = std::fabs(v[i]); + if (c > cmax) { + cmax = c; + imax = i; + } + } + + Vec3d result = Vec3d::Zero(); + int32_t inext = imax + 1; + if (inext == 3) + inext = 0; + + result[imax] = v[inext]; + result[inext] = -v[imax]; + if (unitLength) { + const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext]; + const double invLength = 1.0 / std::sqrt(sqrDistance); + result[imax] *= invLength; + result[inext] *= invLength; + } + return result; +} + +} // namespace Slic3r +} // namespace Measure + +#endif // Slic3r_MeasureUtils_hpp_ + +#endif // ENABLE_MEASURE_GIZMO From e3fd2ca48413dbcf746de4294315e955c320d944 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 11:41:15 +0200 Subject: [PATCH 183/327] Fixed warning --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 1976cbcdab..0d53c69385 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -909,7 +909,7 @@ void GLGizmoMeasure::render_dimensioning() m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; - auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { + auto action_scale = [perform_scale, action_exit](double new_value, double old_value) { perform_scale(new_value, old_value); action_exit(); }; From 2a7611ebafadde4d8366ef86f77465fad14891d1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 13:38:07 +0200 Subject: [PATCH 184/327] Measuring: Gizmo measure - Fixed rendering of selected circle features --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 34 ++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0d53c69385..db33135dda 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -329,7 +329,8 @@ void GLGizmoMeasure::on_render() if (m_mode == EMode::BasicSelection) { std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); - if (m_curr_feature != curr_feature) { + if (m_curr_feature != curr_feature || + (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); @@ -470,7 +471,7 @@ void GLGizmoMeasure::on_render() }; auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - float inv_zoom, bool update_raycasters) { + float inv_zoom, bool update_raycasters_transform) { switch (feature.get_type()) { default: { assert(false); break; } @@ -481,7 +482,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(feature_matrix); @@ -496,7 +497,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); @@ -504,13 +505,20 @@ void GLGizmoMeasure::on_render() // render circle const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { + m_circle.model.set_color(colors.back()); + m_circle.model.render(); auto it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(circle_matrix); } + else { + GLModel circle; + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + circle.init_from(std::move(circle_geometry)); + circle.set_color(colors.back()); + circle.render(); + } break; } case Measure::SurfaceFeatureType::Edge: @@ -523,7 +531,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(point_matrix); @@ -536,7 +544,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(edge_matrix); @@ -550,7 +558,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(m_volume_matrix); m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(m_volume_matrix); @@ -602,8 +610,7 @@ void GLGizmoMeasure::on_render() } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { - std::vector colors; - colors.emplace_back(SELECTED_1ST_COLOR); + const std::vector colors = { SELECTED_1ST_COLOR }; render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), @@ -613,8 +620,7 @@ void GLGizmoMeasure::on_render() } } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { - std::vector colors; - colors.emplace_back(SELECTED_2ND_COLOR); + const std::vector colors = { SELECTED_2ND_COLOR }; render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), From ac167e29adce1c3cc63bd3188d470fa94ec87e1d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 12:53:02 +0200 Subject: [PATCH 185/327] First implementation of SurfaceMesh --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/SurfaceMesh.hpp | 151 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/libslic3r/SurfaceMesh.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a118f6ef39..ac1abe5375 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -253,6 +253,7 @@ set(SLIC3R_SOURCES Surface.hpp SurfaceCollection.cpp SurfaceCollection.hpp + SurfaceMesh.hpp SVG.cpp SVG.hpp Technologies.hpp diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp new file mode 100644 index 0000000000..47d5ed0dcc --- /dev/null +++ b/src/libslic3r/SurfaceMesh.hpp @@ -0,0 +1,151 @@ +#ifndef slic3r_SurfaceMesh_hpp_ +#define slic3r_SurfaceMesh_hpp_ + +#include + +namespace Slic3r { + +class TriangleMesh; + + + +enum Face_index : int; + +class Halfedge_index { + friend class SurfaceMesh; + +public: + Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} + Face_index face() const { return m_face; } + bool is_invalid() const { return int(m_face) < 0; } + bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } + bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } + +private: + Halfedge_index(int face_idx, unsigned char side_idx) : m_face(Face_index(face_idx)), m_side(side_idx) {} + + Face_index m_face; + unsigned char m_side; +}; + + + +class Vertex_index { + friend class SurfaceMesh; + +public: + Vertex_index() : m_face(Face_index(-1)), m_vertex_idx(0) {} + bool is_invalid() const { return int(m_face) < 0; } + bool operator==(const Vertex_index& rhs) const = delete; // Use SurfaceMesh::is_same_vertex. + +private: + Vertex_index(int face_idx, unsigned char vertex_idx) : m_face(Face_index(face_idx)), m_vertex_idx(vertex_idx) {} + + Face_index m_face; + unsigned char m_vertex_idx; +}; + + + +class SurfaceMesh { +public: + explicit SurfaceMesh(const indexed_triangle_set& its) + : m_its(its), + m_face_neighbors(its_face_neighbors_par(its)) + {} + SurfaceMesh(const SurfaceMesh&) = delete; + SurfaceMesh& operator=(const SurfaceMesh&) = delete; + + Vertex_index source(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side); } + Vertex_index target(Halfedge_index h) const { assert(! h.is_invalid()); return Vertex_index(h.m_face, h.m_side == 2 ? 0 : h.m_side + 1); } + Face_index face(Halfedge_index h) const { assert(! h.is_invalid()); return h.m_face; } + + Halfedge_index next(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side + 1) % 3; return h; } + Halfedge_index prev(Halfedge_index h) const { assert(! h.is_invalid()); h.m_side = (h.m_side == 0 ? 2 : h.m_side - 1); return h; } + Halfedge_index halfedge(Vertex_index v) const { return Halfedge_index(v.m_face, (v.m_vertex_idx == 0 ? 2 : v.m_vertex_idx - 1)); } + Halfedge_index halfedge(Face_index f) const { return Halfedge_index(f, 0); } + Halfedge_index opposite(Halfedge_index h) const { + if (h.is_invalid()) + return h; + + int face_idx = m_face_neighbors[h.m_face][h.m_side]; + Halfedge_index h_candidate = halfedge(Face_index(face_idx)); + + if (h_candidate.is_invalid()) + return Halfedge_index(); // invalid + + for (int i=0; i<3; ++i) { + if (is_same_vertex(source(h_candidate), target(h))) { + // Meshes in PrusaSlicer should be fixed enough for the following not to happen. + assert(is_same_vertex(target(h_candidate), source(h))); + return h_candidate; + } + h_candidate = next(h_candidate); + } + return Halfedge_index(); // invalid + } + + Halfedge_index next_around_target(Halfedge_index h) const { return opposite(next(h)); } + Halfedge_index prev_around_target(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : prev(op)); } + Halfedge_index next_around_source(Halfedge_index h) const { Halfedge_index op = opposite(h); return (op.is_invalid() ? Halfedge_index() : next(op)); } + Halfedge_index prev_around_source(Halfedge_index h) const { return opposite(prev(h)); } + Halfedge_index halfedge(Vertex_index source, Vertex_index target) const + { + Halfedge_index hi(source.m_face, source.m_vertex_idx); + assert(! hi.is_invalid()); + + const Vertex_index orig_target = this->target(hi); + Vertex_index current_target = orig_target; + + while (! is_same_vertex(current_target, target)) { + hi = next_around_source(hi); + if (hi.is_invalid()) + break; + current_target = this->target(hi); + if (is_same_vertex(current_target, orig_target)) + return Halfedge_index(); // invalid + } + + return hi; + } + + const stl_vertex& point(Vertex_index v) const { return m_its.vertices[m_its.indices[v.m_face][v.m_vertex_idx]]; } + + size_t degree(Vertex_index v) const + { + Halfedge_index h_first = halfedge(v); + Halfedge_index h = next_around_target(h_first); + size_t degree = 2; + while (! h.is_invalid() && h != h_first) { + h = next_around_target(h); + ++degree; + } + return h.is_invalid() ? 0 : degree - 1; + } + + size_t degree(Face_index f) const { + size_t total = 0; + for (unsigned char i=0; i<3; ++i) { + size_t d = degree(Vertex_index(f, i)); + if (d == 0) + return 0; + total += d; + } + assert(total - 6 >= 0); + return total - 6; // we counted 3 halfedges from f, and one more for each neighbor + } + + bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } + + bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + + + +private: + const std::vector m_face_neighbors; + const indexed_triangle_set& m_its; +}; + +} //namespace Slic3r + +#endif // slic3r_SurfaceMesh_hpp_ From 05e2a831f44d56ab2a7cd5d36ff92f58a185929f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 12:53:34 +0200 Subject: [PATCH 186/327] Added some unit tests (SurfaceMesh) --- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_surface_mesh.cpp | 122 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 tests/libslic3r/test_surface_mesh.cpp diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index a787208074..ed7d8a92be 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(${_TEST_NAME}_tests test_voronoi.cpp test_optimizers.cpp test_png_io.cpp + test_surface_mesh.cpp test_timeutils.cpp test_indexed_triangle_set.cpp test_astar.cpp diff --git a/tests/libslic3r/test_surface_mesh.cpp b/tests/libslic3r/test_surface_mesh.cpp new file mode 100644 index 0000000000..34ff356679 --- /dev/null +++ b/tests/libslic3r/test_surface_mesh.cpp @@ -0,0 +1,122 @@ +#include +#include + + +#include + +using namespace Slic3r; + + +// Generate a broken cube mesh. Face 8 is inverted, face 11 is missing. +indexed_triangle_set its_make_cube_broken(double xd, double yd, double zd) +{ + auto x = float(xd), y = float(yd), z = float(zd); + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3} /*missing face*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; +} + + + +TEST_CASE("SurfaceMesh on a cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube(1., 1., 1.); + SurfaceMesh sm(cube); + const Halfedge_index hi_first = sm.halfedge(Face_index(0)); + Halfedge_index hi = hi_first; + + REQUIRE(! hi_first.is_invalid()); + + SECTION("next / prev halfedge") { + hi = sm.next(hi); + REQUIRE(hi != hi_first); + hi = sm.next(hi); + hi = sm.next(hi); + REQUIRE(hi == hi_first); + hi = sm.prev(hi); + REQUIRE(hi != hi_first); + hi = sm.prev(hi); + hi = sm.prev(hi); + REQUIRE(hi == hi_first); + } + + SECTION("next_around_target") { + // Check that we get to the same halfedge after applying next_around_target + // four times. + const Vertex_index target_vert = sm.target(hi_first); + for (int i=0; i<4;++i) { + hi = sm.next_around_target(hi); + REQUIRE((hi == hi_first) == (i == 3)); + REQUIRE(sm.is_same_vertex(sm.target(hi), target_vert)); + REQUIRE(! sm.is_border(hi)); + } + } + + SECTION("iterate around target and source") { + hi = sm.next_around_target(hi); + hi = sm.prev_around_target(hi); + hi = sm.prev_around_source(hi); + hi = sm.next_around_source(hi); + REQUIRE(hi == hi_first); + } + + SECTION("opposite") { + const Vertex_index target = sm.target(hi); + const Vertex_index source = sm.source(hi); + hi = sm.opposite(hi); + REQUIRE(sm.is_same_vertex(target, sm.source(hi))); + REQUIRE(sm.is_same_vertex(source, sm.target(hi))); + hi = sm.opposite(hi); + REQUIRE(hi == hi_first); + } + + SECTION("halfedges walk") { + for (int i=0; i<4; ++i) { + hi = sm.next(hi); + hi = sm.opposite(hi); + } + REQUIRE(hi == hi_first); + } + + SECTION("point accessor") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + hi = sm.opposite(hi); + hi = sm.prev(hi); + hi = sm.opposite(hi); + REQUIRE(hi.face() == Face_index(6)); + REQUIRE(sm.point(sm.target(hi)).isApprox(cube.vertices[7])); + } +} + + + + +TEST_CASE("SurfaceMesh on a broken cube", "[SurfaceMesh]") { + indexed_triangle_set cube = its_make_cube_broken(1., 1., 1.); + SurfaceMesh sm(cube); + + SECTION("Check inverted face") { + Halfedge_index hi = sm.halfedge(Face_index(8)); + for (int i=0; i<3; ++i) { + REQUIRE(! hi.is_invalid()); + REQUIRE(sm.is_border(hi)); + } + REQUIRE(hi == sm.halfedge(Face_index(8))); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } + + SECTION("missing face") { + Halfedge_index hi = sm.halfedge(Face_index(0)); + for (int i=0; i<3; ++i) + hi = sm.next_around_source(hi); + hi = sm.next(hi); + REQUIRE(sm.is_border(hi)); + REQUIRE(! hi.is_invalid()); + hi = sm.opposite(hi); + REQUIRE(hi.is_invalid()); + } +} From 8833fb7ab4f6c60c65a12ceb48a16f4bf899deec Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Jun 2022 12:19:47 +0200 Subject: [PATCH 187/327] SurfaceMesh testing (to be reverted later) Fixed conflicts while rebasing to master --- src/libslic3r/TriangleMesh.cpp | 11 +- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 252 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp | 2 + 3 files changed, 147 insertions(+), 118 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index df820fac92..ef96e2400f 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -885,13 +885,20 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor indexed_triangle_set its_make_cube(double xd, double yd, double zd) { auto x = float(xd), y = float(yd), z = float(zd); - return { + /*return { { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} }, { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - }; + };*/ + return { + { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, + {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, + {2, 5, 6}, {2, 5, 3}, {4, 0, 3}, /*{4, 3, 5}*/ }, + { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, + {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } + }; } indexed_triangle_set its_make_prism(float width, float length, float height) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 3af58dd960..70bbc92107 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -10,6 +10,7 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/SurfaceMesh.hpp" #include @@ -21,9 +22,31 @@ namespace GUI { static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; + + + +// TESTING: +static Halfedge_index hi; +static bool hi_initialized = false; +static std::unique_ptr sm_ptr; +static Vertex_index src; +static Vertex_index tgt; + + + + + + GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + indexed_triangle_set a = its_make_cone(0.05, .2); + its_rotate_x(a, M_PI); + its_translate(a, stl_vertex(0., 0., .8)); + indexed_triangle_set b = its_make_cylinder(.02, 0.8); + its_merge(a, b); + arrow.init_from(a); +} bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) { @@ -37,9 +60,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; Selection &selection = m_parent.get_selection(); if (selection.is_single_full_instance()) { - // Rotate the object so the normal points downward: - selection.flattening_rotate(m_planes[m_hover_id].normal); - m_parent.do_rotate(L("Gizmo-Place on Face")); + hi = sm_ptr->halfedge(Face_index(m_hover_id)); } return true; } @@ -101,6 +122,18 @@ void GLGizmoFlatten::on_render() const Selection& selection = m_parent.get_selection(); #if ENABLE_LEGACY_OPENGL_REMOVAL + + + + if (!hi_initialized) { + const indexed_triangle_set & its = m_c->selection_info()->model_object()->volumes.front()->mesh().its; + sm_ptr.reset(new SurfaceMesh(its)); + hi = sm_ptr->halfedge(Face_index(0)); + hi_initialized = true; + } + SurfaceMesh & sm = *sm_ptr; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; @@ -136,8 +169,13 @@ void GLGizmoFlatten::on_render() m_planes[i].vbo.model.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); m_planes[i].vbo.model.render(); #else - m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbo.render(); + int cur_face = hi.is_invalid() ? 1000000 : sm.face(hi); + for (int i = 0; i < m_planes.size(); ++i) { + m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + if (i == cur_face) + m_planes[i].vbo.set_color(i == m_hover_id ? ColorRGBA(.5f, 0.f, 0.f, 1.f) : ColorRGBA(1.f, 0.f, 0.f, 1.f)); + m_planes[i].vbo.render(); + } #endif // ENABLE_RAYCAST_PICKING #else glsafe(::glColor4fv(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR.data() : DEFAULT_PLANE_COLOR.data())); @@ -150,6 +188,90 @@ void GLGizmoFlatten::on_render() #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } + + + + + ///////////////// + //////////////// + ////////////////// + + auto draw_arrow = [&](const Vec3d& from, const Vec3d& to) -> void { + Vec3d desired_pos = from; + Vec3d desired_dir = to - from; + double desired_len = desired_dir.norm(); + desired_dir.normalize(); + + Transform3d m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + m.translate(desired_pos); + Eigen::Quaterniond q; + Transform3d rot = Transform3d::Identity(); + rot.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), desired_dir).toRotationMatrix(); + Transform3d sc = Transform3d::Identity(); + sc.scale(desired_len); + m = m * sc * rot; + + const Camera & camera = wxGetApp().plater()->get_camera(); + Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + arrow.render(); + }; + + m_imgui->begin(std::string("DEBUG")); + bool invalid = hi.is_invalid(); + if (invalid) { + if (m_imgui->button(std::string("HALFEDGE INVALID (Click to reset)"))) + hi = sm.halfedge(Face_index(0)); + } + else { + m_imgui->text(sm.is_border(hi) ? "BORDER HALFEDGE !" : "Halfedge is not border"); + m_imgui->text((std::string("Face: ") + std::to_string(int(hi.face()))).c_str()); + m_imgui->text(std::string("Target degree:" + std::to_string(sm.degree(sm.target(hi))))); + m_imgui->text(std::string("Face degree:" + std::to_string(sm.degree(sm.face(hi))))); + } + m_imgui->disabled_begin(invalid); + if (m_imgui->button(std::string("next"))) + hi = sm.next(hi); + if (m_imgui->button(std::string("prev"))) + hi = sm.prev(hi); + if (m_imgui->button(std::string("opposite"))) + hi = sm.opposite(hi); + if (m_imgui->button(std::string("next_around_target"))) + hi = sm.next_around_target(hi); + if (m_imgui->button(std::string("prev_around_target"))) + hi = sm.prev_around_target(hi); + if (m_imgui->button(std::string("next_around_source"))) + hi = sm.next_around_source(hi); + if (m_imgui->button(std::string("prev_around_source"))) + hi = sm.prev_around_source(hi); + if (m_imgui->button(std::string("remember one"))) + src = sm.target(hi); + if (m_imgui->button(std::string("switch to halfedge"))) { + tgt = sm.target(hi); + hi = sm.halfedge(src, tgt); + } + + if (invalid) + m_imgui->disabled_end(); + m_imgui->end(); + + if (!hi.is_invalid()) { + Vec3d a = sm.point(sm.source(hi)).cast(); + Vec3d b = sm.point(sm.target(hi)).cast(); + draw_arrow(a, b); + } + + + ///////////////// + //////////////// + ////////////////// + + + + + glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -253,11 +375,11 @@ void GLGizmoFlatten::update_planes() for (const ModelVolume* vol : mo->volumes) { if (vol->type() != ModelVolumeType::MODEL_PART) continue; - TriangleMesh vol_ch = vol->get_convex_hull(); + TriangleMesh vol_ch = vol->mesh(); //vol->get_convex_hull(); vol_ch.transform(vol->get_matrix()); ch.merge(vol_ch); } - ch = ch.convex_hull_3d(); + //ch = ch.convex_hull_3d(); m_planes.clear(); #if ENABLE_RAYCAST_PICKING on_unregister_raycasters_for_picking(); @@ -281,47 +403,17 @@ void GLGizmoFlatten::update_planes() std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; - int facet_idx = 0; - while (1) { - // Find next unvisited triangle: - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &face_normals[facet_idx]; - m_planes.emplace_back(); - break; - } - if (facet_idx == num_of_facets) - break; // Everything was visited already - - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = face_normals[facet_idx]; - if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) { - const Vec3i face = ch.its.indices[facet_idx]; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - m_planes.back().normal = normal_ptr->cast(); + for (size_t i = 0; i < ch.its.indices.size(); ++i) { + const Vec3i & face = ch.its.indices[i]; + m_planes.emplace_back(); + for (int j = 0; j < 3; ++j) + m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); + m_planes.back().normal = face_normals[i].cast(); Pointf3s& verts = m_planes.back().vertices; // Now we'll transform all the points into world coordinates, so that the areas, angles and distances // make real sense. verts = transform(verts, inst_matrix); - - // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway): - if (verts.size() == 3 && - ((verts[0] - verts[1]).norm() < minimal_side - || (verts[0] - verts[2]).norm() < minimal_side - || (verts[1] - verts[2]).norm() < minimal_side)) - m_planes.pop_back(); } // Let's prepare transformation of the normal vector from mesh to instance coordinates. @@ -353,84 +445,12 @@ void GLGizmoFlatten::update_planes() polygon = Slic3r::Geometry::convex_hull(polygon); polygon = transform(polygon, tr.inverse()); - // Calculate area of the polygons and discard ones that are too small - float& area = m_planes[polygon_id].area; - area = 0.f; - for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula - area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1); - area = 0.5f * std::abs(area); - - bool discard = false; - if (area < minimal_area) - discard = true; - else { - // We also check the inner angles and discard polygons with angles smaller than the following threshold - const double angle_threshold = ::cos(10.0 * (double)PI / 180.0); - - for (unsigned int i = 0; i < polygon.size(); ++i) { - const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1]; - const Vec3d& curr = polygon[i]; - const Vec3d& next = polygon[(i == polygon.size() - 1) ? 0 : i + 1]; - - if ((prec - curr).normalized().dot((next - curr).normalized()) > angle_threshold) { - discard = true; - break; - } - } - } - - if (discard) { - m_planes[polygon_id--] = std::move(m_planes.back()); - m_planes.pop_back(); - continue; - } - // We will shrink the polygon a little bit so it does not touch the object edges: Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); centroid /= (double)polygon.size(); for (auto& vertex : polygon) vertex = 0.9f*vertex + 0.1f*centroid; - // Polygon is now simple and convex, we'll round the corners to make them look nicer. - // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex - // towards their average (controlled by 'aggressivity'). This is repeated k times. - // In next iterations, the neighbours are not always taken at the middle (to increase the - // rounding effect at the corners, where we need it most). - const unsigned int k = 10; // number of iterations - const float aggressivity = 0.2f; // agressivity - const unsigned int N = polygon.size(); - std::vector> neighbours; - if (k != 0) { - Pointf3s points_out(2*k*N); // vector long enough to store the future vertices - for (unsigned int j=0; j vertices; // should be in fact local in update_planes() #if ENABLE_LEGACY_OPENGL_REMOVAL From bd63320a0028e0ec66d4b8148438adf15896688a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 3 Jun 2022 15:37:11 +0200 Subject: [PATCH 188/327] Measuring: separated another gizmo --- resources/icons/measure.svg | 13 + src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 388 ++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 75 +++++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 7 files changed, 482 insertions(+) create mode 100644 resources/icons/measure.svg create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp diff --git a/resources/icons/measure.svg b/resources/icons/measure.svg new file mode 100644 index 0000000000..275c522251 --- /dev/null +++ b/resources/icons/measure.svg @@ -0,0 +1,13 @@ + + + Layer 1 + + + + + + + + + + \ No newline at end of file diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index 47d5ed0dcc..e8ada4cd80 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -138,6 +138,7 @@ public: bool is_border(Halfedge_index h) const { return m_face_neighbors[h.m_face][h.m_side] == -1; } bool is_same_vertex(const Vertex_index& a, const Vertex_index& b) const { return m_its.indices[a.m_face][a.m_vertex_idx] == m_its.indices[b.m_face][b.m_vertex_idx]; } + Vec3i get_face_neighbors(Face_index face_id) const { assert(int(face_id) < int(m_face_neighbors.size())); return m_face_neighbors[face_id]; } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5a5e2bb5af..cd1e4ba2b3 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -63,6 +63,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoSimplify.hpp GUI/Gizmos/GLGizmoMmuSegmentation.cpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp + GUI/Gizmos/GLGizmoMeasure.cpp + GUI/Gizmos/GLGizmoMeasure.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLModel.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp new file mode 100644 index 0000000000..572ea75dbe --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -0,0 +1,388 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoMeasure.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" + +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" + +#include "libslic3r/Geometry/ConvexHull.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/SurfaceMesh.hpp" + +#include + +#include + +namespace Slic3r { +namespace GUI { + +static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; +static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; + +GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) +{} + + + +bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) +{ + if (mouse_event.Moving()) { + // only for sure + m_mouse_left_down = false; + return false; + } + if (mouse_event.LeftDown()) { + if (m_hover_id != -1) { + m_mouse_left_down = true; + Selection &selection = m_parent.get_selection(); + if (selection.is_single_full_instance()) { + // Rotate the object so the normal points downward: + selection.flattening_rotate(m_planes[m_hover_id].normal); + m_parent.do_rotate(L("Gizmo-Place on Face")); + } + return true; + } + + // fix: prevent restart gizmo when reselect object + // take responsibility for left up + if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; + + } else if (mouse_event.LeftUp()) { + if (m_mouse_left_down) { + // responsible for mouse left up after selecting plane + m_mouse_left_down = false; + return true; + } + } else if (mouse_event.Leaving()) { + m_mouse_left_down = false; + } + return false; +} + + + +void GLGizmoMeasure::data_changed() +{ + const Selection & selection = m_parent.get_selection(); + const ModelObject *model_object = nullptr; + if (selection.is_single_full_instance() || + selection.is_from_single_object() ) { + model_object = selection.get_model()->objects[selection.get_object_idx()]; + } + set_flattening_data(model_object); +} + + + +bool GLGizmoMeasure::on_init() +{ + // FIXME m_shortcut_key = WXK_CONTROL_F; + return true; +} + + + +void GLGizmoMeasure::on_set_state() +{ +} + + + +CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const +{ + return CommonGizmosDataID::SelectionInfo; +} + + + +std::string GLGizmoMeasure::on_get_name() const +{ + return _u8L("Measure"); +} + + + +bool GLGizmoMeasure::on_is_activable() const +{ + // 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 GLGizmoMeasure::on_render() +{ + const Selection& selection = m_parent.get_selection(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + + if (selection.is_single_full_instance()) { + const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (this->is_plane_update_necessary()) + update_planes(); + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + m_planes[i].vbo.render(); + } + } + + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + + shader->stop_using(); +} + + + + + +#if ! ENABLE_LEGACY_OPENGL_REMOVAL + #error NOT IMPLEMENTED +#endif +#if ! ENABLE_GL_SHADERS_ATTRIBUTES + #error NOT IMPLEMENTED +#endif + + + + + +void GLGizmoMeasure::on_render_for_picking() +{ + const Selection& selection = m_parent.get_selection(); + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); + + if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { + const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d view_model_matrix = camera.get_view_matrix() * + Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + + shader->set_uniform("view_model_matrix", view_model_matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + if (this->is_plane_update_necessary()) + update_planes(); + for (int i = 0; i < (int)m_planes.size(); ++i) { + m_planes[i].vbo.set_color(picking_color_component(i)); + m_planes[i].vbo.render(); + } + } + + glsafe(::glEnable(GL_CULL_FACE)); + + shader->stop_using(); +} + + + +void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +{ + if (model_object != m_old_model_object) { + m_planes.clear(); + m_planes_valid = false; + } +} + + + +void GLGizmoMeasure::update_planes() +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + TriangleMesh ch; + for (const ModelVolume* vol : mo->volumes) { + if (vol->type() != ModelVolumeType::MODEL_PART) + continue; + TriangleMesh vol_ch = vol->mesh(); + vol_ch.transform(vol->get_matrix()); + ch.merge(vol_ch); + } + m_planes.clear(); + const Transform3d& inst_matrix = mo->instances.front()->get_matrix(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal. + // This part is still performed in mesh coordinate system. + const int num_of_facets = ch.facets_count(); + std::vector face_to_plane(num_of_facets, 0); + const std::vector face_normals = its_face_normals(ch.its); + const std::vector face_neighbors = its_face_neighbors(ch.its); + std::vector facet_queue(num_of_facets, 0); + std::vector facet_visited(num_of_facets, false); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + int facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + while (1) { + // Find next unvisited triangle: + for (; facet_idx < num_of_facets; ++ facet_idx) + if (!facet_visited[facet_idx]) { + facet_queue[facet_queue_cnt ++] = facet_idx; + facet_visited[facet_idx] = true; + normal_ptr = &face_normals[facet_idx]; + face_to_plane[facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { + const Vec3i& face = ch.its.indices[facet_idx]; + for (int j=0; j<3; ++j) + m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); + + facet_visited[facet_idx] = true; + face_to_plane[facet_idx] = m_planes.size() - 1; + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + m_planes.back().normal = normal_ptr->cast(); + + Pointf3s& verts = m_planes.back().vertices; + // Now we'll transform all the points into world coordinates, so that the areas, angles and distances + // make real sense. + verts = transform(verts, inst_matrix); + } + + // Let's prepare transformation of the normal vector from mesh to instance coordinates. + Geometry::Transformation t(inst_matrix); + Vec3d scaling = t.get_scaling_factor(); + t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); + + // Now we'll go through all the polygons, transform the points into xy plane to process them: + for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { + Pointf3s& polygon = m_planes[polygon_id].vertices; + const Vec3d& normal = m_planes[polygon_id].normal; + + // transform the normal according to the instance matrix: + Vec3d normal_transformed = t.get_matrix() * normal; + + // We are going to rotate about z and y to flatten the plane + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); + polygon = transform(polygon, m); + + // Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since + // it works in fixed point representation, we will rescale the polygon to avoid overflows. + // And yes, it is a nasty thing to do. Whoever has time is free to refactor. + Vec3d bb_size = BoundingBoxf3(polygon).size(); + float sf = std::min(1./bb_size(0), 1./bb_size(1)); + Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); + polygon = transform(polygon, tr); + polygon = Slic3r::Geometry::convex_hull(polygon); + polygon = transform(polygon, tr.inverse()); + + // We will shrink the polygon a little bit so it does not touch the object edges: + Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); + centroid /= (double)polygon.size(); + for (auto& vertex : polygon) + vertex = 0.95f*vertex + 0.05f*centroid; + + // Raise a bit above the object surface to avoid flickering: + for (auto& b : polygon) + b(2) += 0.1f; + + // Transform back to 3D (and also back to mesh coordinates) + polygon = transform(polygon, inst_matrix.inverse() * m.inverse()); + } + + // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): + std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); + m_planes.resize(std::min((int)m_planes.size(), 254)); + + // Planes are finished - let's save what we calculated it from: + m_volumes_matrices.clear(); + m_volumes_types.clear(); + for (const ModelVolume* vol : mo->volumes) { + m_volumes_matrices.push_back(vol->get_matrix()); + m_volumes_types.push_back(vol->type()); + } + m_first_instance_scale = mo->instances.front()->get_scaling_factor(); + m_first_instance_mirror = mo->instances.front()->get_mirror(); + m_old_model_object = mo; + + // And finally create respective VBOs. The polygon is convex with + // the vertices in order, so triangulation is trivial. + for (auto& plane : m_planes) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(plane.vertices.size()); + init_data.reserve_indices(plane.vertices.size()); + // vertices + indices + for (size_t i = 0; i < plane.vertices.size(); ++i) { + init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); + init_data.add_index((unsigned int)i); + } + plane.vbo.init_from(std::move(init_data)); + + // FIXME: vertices should really be local, they need not + // persist now when we use VBOs + plane.vertices.clear(); + plane.vertices.shrink_to_fit(); + } + + m_planes_valid = true; +} + + + +bool GLGizmoMeasure::is_plane_update_necessary() const +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + if (m_state != On || ! mo || mo->instances.empty()) + return false; + + if (! m_planes_valid || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) + return true; + + // We want to recalculate when the scale changes - some planes could (dis)appear. + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + return true; + + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) + return true; + + return false; +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp new file mode 100644 index 0000000000..9bf87a29f6 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -0,0 +1,75 @@ +#ifndef slic3r_GLGizmoMeasure_hpp_ +#define slic3r_GLGizmoMeasure_hpp_ + +#include "GLGizmoBase.hpp" +#if ENABLE_LEGACY_OPENGL_REMOVAL +#include "slic3r/GUI/GLModel.hpp" +#else +#include "slic3r/GUI/3DScene.hpp" +#endif // ENABLE_LEGACY_OPENGL_REMOVAL + + +namespace Slic3r { + +enum class ModelVolumeType : int; + + +namespace GUI { + + +class GLGizmoMeasure : public GLGizmoBase +{ +// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. + +private: + + struct PlaneData { + std::vector vertices; // should be in fact local in update_planes() + std::vector borders_facets; + GLModel vbo; + Vec3d normal; + float area; + }; + + // This holds information to decide whether recalculation is necessary: + std::vector m_volumes_matrices; + std::vector m_volumes_types; + Vec3d m_first_instance_scale; + Vec3d m_first_instance_mirror; + + std::vector m_planes; + std::vector m_face_to_plane; + bool m_mouse_left_down = false; // for detection left_up of this gizmo + bool m_planes_valid = false; + const ModelObject* m_old_model_object = nullptr; + std::vector instances_matrices; + + void update_planes(); + bool is_plane_update_necessary() const; + void set_flattening_data(const ModelObject* model_object); + +public: + GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + + /// + /// Apply rotation on select plane + /// + /// Keep information about mouse click + /// Return True when use the information otherwise False. + bool on_mouse(const wxMouseEvent &mouse_event) override; + + void data_changed() override; +protected: + bool on_init() override; + std::string on_get_name() const override; + bool on_is_activable() const override; + void on_render() override; + void on_render_for_picking() override; + void on_set_state() override; + CommonGizmosDataID on_get_requirements() const override; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 8759e880d7..6d25f84fbc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,6 +21,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -106,6 +107,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); + m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 8a708f62a6..a759d911a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -80,6 +80,7 @@ public: Seam, MmuSegmentation, Simplify, + Measure, Undefined }; From 2df91985520bc0755b203cc5912130df419604c7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 21 Jun 2022 12:47:47 +0200 Subject: [PATCH 189/327] Measuring: Initial plane detection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 144 +++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 572ea75dbe..129407764c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -127,6 +127,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); + glsafe(::glLineWidth(5.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -227,15 +228,14 @@ void GLGizmoMeasure::update_planes() // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. - const int num_of_facets = ch.facets_count(); - std::vector face_to_plane(num_of_facets, 0); + const size_t num_of_facets = ch.facets_count(); + std::vector face_to_plane(num_of_facets, size_t(-1)); const std::vector face_normals = its_face_normals(ch.its); const std::vector face_neighbors = its_face_neighbors(ch.its); std::vector facet_queue(num_of_facets, 0); - std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; - int facet_idx = 0; + size_t seed_facet_idx = 0; auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); @@ -243,16 +243,15 @@ void GLGizmoMeasure::update_planes() while (1) { // Find next unvisited triangle: - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &face_normals[facet_idx]; - face_to_plane[facet_idx] = m_planes.size(); + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + face_to_plane[seed_facet_idx] = m_planes.size(); m_planes.emplace_back(); break; } - if (facet_idx == num_of_facets) + if (seed_facet_idx == num_of_facets) break; // Everything was visited already while (facet_queue_cnt > 0) { @@ -260,70 +259,69 @@ void GLGizmoMeasure::update_planes() const stl_normal& this_normal = face_normals[facet_idx]; if (is_same_normal(this_normal, *normal_ptr)) { const Vec3i& face = ch.its.indices[facet_idx]; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - facet_visited[facet_idx] = true; face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } - m_planes.back().normal = normal_ptr->cast(); - Pointf3s& verts = m_planes.back().vertices; - // Now we'll transform all the points into world coordinates, so that the areas, angles and distances - // make real sense. - verts = transform(verts, inst_matrix); + m_planes.back().normal = normal_ptr->cast(); } + assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + SurfaceMesh sm(ch.its); + for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { + //int plane_id = 5; { + const auto& facets = m_planes[plane_id].facets; + std::vector pts; + for (int face_id=0; face_id{ sm.point(sm.source(he)).cast() }); + Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + } + } + + + // DEBUGGING: + //m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); + + + + + // Let's prepare transformation of the normal vector from mesh to instance coordinates. Geometry::Transformation t(inst_matrix); Vec3d scaling = t.get_scaling_factor(); t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); - // Now we'll go through all the polygons, transform the points into xy plane to process them: - for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { - Pointf3s& polygon = m_planes[polygon_id].vertices; - const Vec3d& normal = m_planes[polygon_id].normal; - - // transform the normal according to the instance matrix: - Vec3d normal_transformed = t.get_matrix() * normal; - - // We are going to rotate about z and y to flatten the plane - Eigen::Quaterniond q; - Transform3d m = Transform3d::Identity(); - m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(normal_transformed, Vec3d::UnitZ()).toRotationMatrix(); - polygon = transform(polygon, m); - - // Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since - // it works in fixed point representation, we will rescale the polygon to avoid overflows. - // And yes, it is a nasty thing to do. Whoever has time is free to refactor. - Vec3d bb_size = BoundingBoxf3(polygon).size(); - float sf = std::min(1./bb_size(0), 1./bb_size(1)); - Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f)); - polygon = transform(polygon, tr); - polygon = Slic3r::Geometry::convex_hull(polygon); - polygon = transform(polygon, tr.inverse()); - - // We will shrink the polygon a little bit so it does not touch the object edges: - Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); - centroid /= (double)polygon.size(); - for (auto& vertex : polygon) - vertex = 0.95f*vertex + 0.05f*centroid; - - // Raise a bit above the object surface to avoid flickering: - for (auto& b : polygon) - b(2) += 0.1f; - - // Transform back to 3D (and also back to mesh coordinates) - polygon = transform(polygon, inst_matrix.inverse() * m.inverse()); - } - - // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): - std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); - m_planes.resize(std::min((int)m_planes.size(), 254)); + // Planes are finished - let's save what we calculated it from: m_volumes_matrices.clear(); @@ -339,21 +337,23 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::TriangleFan, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(plane.vertices.size()); - init_data.reserve_indices(plane.vertices.size()); - // vertices + indices - for (size_t i = 0; i < plane.vertices.size(); ++i) { - init_data.add_vertex((Vec3f)plane.vertices[i].cast(), (Vec3f)plane.normal.cast()); - init_data.add_index((unsigned int)i); + for (auto& vertices : plane.borders) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; + init_data.reserve_vertices(vertices.size()); + init_data.reserve_indices(vertices.size()); + // vertices + indices + for (size_t i = 0; i < vertices.size(); ++i) { + init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); + init_data.add_index((unsigned int)i); + } + plane.vbo.init_from(std::move(init_data)); } - plane.vbo.init_from(std::move(init_data)); // FIXME: vertices should really be local, they need not // persist now when we use VBOs - plane.vertices.clear(); - plane.vertices.shrink_to_fit(); + plane.borders.clear(); + plane.borders.shrink_to_fit(); } m_planes_valid = true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9bf87a29f6..87ad73d8c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,8 +24,8 @@ class GLGizmoMeasure : public GLGizmoBase private: struct PlaneData { - std::vector vertices; // should be in fact local in update_planes() - std::vector borders_facets; + std::vector> borders; // should be in fact local in update_planes() + std::vector facets; GLModel vbo; Vec3d normal; float area; From 7ae40e281b531ad5e21bc8200c8274c227bfb849 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 22 Jun 2022 10:34:58 +0200 Subject: [PATCH 190/327] Measuring: Simple visualization --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 62 ++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 129407764c..de3fc398b9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -17,15 +17,13 @@ namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; -static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; +static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.9f }; +static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1.f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) {} - - bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { if (mouse_event.Moving()) { @@ -139,9 +137,29 @@ void GLGizmoMeasure::on_render() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); - for (int i = 0; i < (int)m_planes.size(); ++i) { - m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbo.render(); + + m_imgui->begin(std::string("DEBUG")); + if (m_imgui->button("<-")) + --m_currently_shown_plane; + ImGui::SameLine(); + if (m_imgui->button("->")) + ++m_currently_shown_plane; + m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, int(m_planes.size())-1); + m_imgui->text(std::to_string(m_currently_shown_plane)); + m_imgui->end(); + + + //for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_currently_shown_plane; + std::cout << m_hover_id << "\t" << m_currently_shown_plane << "\t" << std::endl; + if (i < m_planes.size()) { + for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { + m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + if (j == m_hover_id) + m_planes[i].vbos[j].render(); + std::cout << " * " << j; + } + std::cout <get_instance_transformation().get_matrix(); @@ -189,9 +208,13 @@ void GLGizmoMeasure::on_render_for_picking() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); if (this->is_plane_update_necessary()) update_planes(); - for (int i = 0; i < (int)m_planes.size(); ++i) { - m_planes[i].vbo.set_color(picking_color_component(i)); - m_planes[i].vbo.render(); + //for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_currently_shown_plane; + if (i < m_planes.size()) { + for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { + m_planes[i].vbos[j].set_color(picking_color_component(j)); + m_planes[i].vbos[j].render(); + } } } @@ -277,7 +300,7 @@ void GLGizmoMeasure::update_planes() for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; - std::vector pts; + std::vector pts; for (int face_id=0; face_id{ sm.point(sm.source(he)).cast() }); + pts.emplace_back(sm.point(sm.source(he)).cast()); Vertex_index target = sm.target(he); const Halfedge_index he_start = he; @@ -303,14 +326,20 @@ void GLGizmoMeasure::update_planes() while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) he = sm.next_around_target(he); he = sm.opposite(he); - m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + pts.emplace_back(sm.point(sm.source(he)).cast()); } while (he != he_start); + + if (pts.size() != 1) { + m_planes[plane_id].borders.emplace_back(pts); + pts.clear(); + } + } } // DEBUGGING: - //m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); + m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); @@ -337,7 +366,7 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. for (auto& plane : m_planes) { - for (auto& vertices : plane.borders) { + for (const auto& vertices : plane.borders) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(vertices.size()); @@ -347,7 +376,8 @@ void GLGizmoMeasure::update_planes() init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); init_data.add_index((unsigned int)i); } - plane.vbo.init_from(std::move(init_data)); + plane.vbos.emplace_back(); + plane.vbos.back().init_from(std::move(init_data)); } // FIXME: vertices should really be local, they need not diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 87ad73d8c6..32b43e6f84 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,10 +23,12 @@ class GLGizmoMeasure : public GLGizmoBase private: + int m_currently_shown_plane = 0; + struct PlaneData { std::vector> borders; // should be in fact local in update_planes() std::vector facets; - GLModel vbo; + std::vector vbos; Vec3d normal; float area; }; From 70ea995f4a13a64975ae98642da3653b0f183711 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Jul 2022 15:51:51 +0200 Subject: [PATCH 191/327] Measuring: First steps on extracting features --- src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 204 ++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 20 ++- 3 files changed, 183 insertions(+), 42 deletions(-) diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index e8ada4cd80..a4b261ceb9 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -17,6 +17,7 @@ class Halfedge_index { public: Halfedge_index() : m_face(Face_index(-1)), m_side(0) {} Face_index face() const { return m_face; } + unsigned char side() const { return m_side; } bool is_invalid() const { return int(m_face) < 0; } bool operator!=(const Halfedge_index& rhs) const { return ! ((*this) == rhs); } bool operator==(const Halfedge_index& rhs) const { return m_face == rhs.m_face && m_side == rhs.m_side; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index de3fc398b9..6c95e70b7f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -22,7 +22,10 @@ static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1 GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{} +{ + m_vbo_sphere.init_from(its_make_sphere(1., M_PI/32.)); + m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); +} bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { @@ -125,7 +128,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); - glsafe(::glLineWidth(5.f)); + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -144,22 +147,47 @@ void GLGizmoMeasure::on_render() ImGui::SameLine(); if (m_imgui->button("->")) ++m_currently_shown_plane; - m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, int(m_planes.size())-1); - m_imgui->text(std::to_string(m_currently_shown_plane)); + m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); + m_imgui->text(std::to_string(m_currently_shown_plane)); + m_imgui->checkbox(wxString("Show all"), m_show_all); m_imgui->end(); - //for (int i = 0; i < (int)m_planes.size(); ++i) { - int i = m_currently_shown_plane; - std::cout << m_hover_id << "\t" << m_currently_shown_plane << "\t" << std::endl; - if (i < m_planes.size()) { + int i = m_show_all ? 0 : m_currently_shown_plane; + for (int i = 0; i < (int)m_planes.size(); ++i) { + // Render all the borders. for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - if (j == m_hover_id) m_planes[i].vbos[j].render(); - std::cout << " * " << j; } - std::cout <::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); + view_feature_matrix *= q; + view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); + m_vbo_cylinder.render(); + } + + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + + + shader->set_uniform("view_model_matrix", view_model_matrix); + } + + if (! m_show_all) + break; } } @@ -196,7 +224,7 @@ void GLGizmoMeasure::on_render_for_picking() glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_BLEND)); - glsafe(::glLineWidth(5.f)); + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -235,6 +263,73 @@ void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) +{ + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + const double edge_threshold = 10. * (M_PI/180.); + + + + for (const std::vector& border : plane.borders) { + assert(border.size() > 1); + assert(! border.front().isApprox(border.back())); + double last_angle = 0.; + size_t first_idx = 0; + + for (size_t i=0; i edge_threshold || i == border.size() - 1) { + // Current feature ended. Save it and remember current point as beginning of the next. + bool is_line = (i == first_idx + 1); + plane.surface_features.emplace_back(SurfaceFeature{ + is_line ? SurfaceFeature::Line : SurfaceFeature::Circle, + is_line ? border[first_idx] : border[first_idx], // FIXME + border[i], + 0. // FIXME + }); + first_idx = i; + } else if (Slic3r::is_approx(angle, last_angle)) { + // possibly a segment of a circle + } else { + first_idx = i; + + } + } + last_angle = angle; + } + + // FIXME: Possibly merge the first and last feature. + + + + std::cout << "==================== " << std::endl; + } + + + for (const SurfaceFeature& f : plane.surface_features) { + std::cout << "- detected " << (f.type == SurfaceFeature::Line ? "Line" : "Circle") << std::endl; + std::cout<< f.pos << std::endl << std::endl << f.endpoint << std::endl; + std::cout << "----------------" << std::endl; + } + + + +} + + + void GLGizmoMeasure::update_planes() { const ModelObject* mo = m_c->selection_info()->model_object(); @@ -292,6 +387,7 @@ void GLGizmoMeasure::update_planes() } m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); @@ -300,40 +396,57 @@ void GLGizmoMeasure::update_planes() for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; - std::vector pts; + m_planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + for (int face_id=0; face_id()); - Vertex_index target = sm.target(he); - const Halfedge_index he_start = he; - - do { + // he is the first halfedge on the border. Now walk around and append the points. const Halfedge_index he_orig = he; - he = sm.next_around_target(he); - while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) - he = sm.next_around_target(he); - he = sm.opposite(he); - pts.emplace_back(sm.point(sm.source(he)).cast()); - } while (he != he_start); + m_planes[plane_id].borders.emplace_back(); + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; - if (pts.size() != 1) { - m_planes[plane_id].borders.emplace_back(pts); - pts.clear(); + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + m_planes[plane_id].borders.back().resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + + if (m_planes[plane_id].borders.back().size() == 1) + m_planes[plane_id].borders.pop_back(); } - } } @@ -365,8 +478,8 @@ void GLGizmoMeasure::update_planes() // And finally create respective VBOs. The polygon is convex with // the vertices in order, so triangulation is trivial. - for (auto& plane : m_planes) { - for (const auto& vertices : plane.borders) { + for (PlaneData& plane : m_planes) { + for (std::vector& vertices : plane.borders) { GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.reserve_vertices(vertices.size()); @@ -378,8 +491,17 @@ void GLGizmoMeasure::update_planes() } plane.vbos.emplace_back(); plane.vbos.back().init_from(std::move(init_data)); + vertices.pop_back(); // first and last are the same } + static int n=0; + std::cout << "==================== " << std::endl; + std::cout << "==================== " << std::endl; + std::cout << "==================== " << std::endl; + std::cout << "Plane num. " << n++ << std::endl; + extract_features(plane); + + // FIXME: vertices should really be local, they need not // persist now when we use VBOs plane.borders.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 32b43e6f84..3099366736 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,15 +24,33 @@ class GLGizmoMeasure : public GLGizmoBase private: int m_currently_shown_plane = 0; + bool m_show_all = false; + + GLModel m_vbo_sphere; + GLModel m_vbo_cylinder; + + struct SurfaceFeature { + enum Type { + Circle, + Line + }; + Type type; + Vec3d pos; + Vec3d endpoint; // for type == Line + double radius; // for type == Circle; + }; struct PlaneData { - std::vector> borders; // should be in fact local in update_planes() std::vector facets; + std::vector> borders; // should be in fact local in update_planes() + std::vector surface_features; std::vector vbos; Vec3d normal; float area; }; + static void extract_features(PlaneData& plane); + // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; From 0e372b8eb2a52eb34db60bb7e1f3573b5b9feb02 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 7 Jul 2022 12:30:26 +0200 Subject: [PATCH 192/327] Measuring: Improved visualization --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 50 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 ++- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6c95e70b7f..0cb1067c39 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -149,12 +149,15 @@ void GLGizmoMeasure::on_render() ++m_currently_shown_plane; m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); m_imgui->text(std::to_string(m_currently_shown_plane)); - m_imgui->checkbox(wxString("Show all"), m_show_all); + m_imgui->checkbox(wxString("Show all"), m_show_all_planes); + m_imgui->checkbox(wxString("Show points"), m_show_points); + m_imgui->checkbox(wxString("Show edges"), m_show_edges); + m_imgui->checkbox(wxString("Show circles"), m_show_circles); m_imgui->end(); - int i = m_show_all ? 0 : m_currently_shown_plane; - for (int i = 0; i < (int)m_planes.size(); ++i) { + int i = m_show_all_planes ? 0 : m_currently_shown_plane; + for (; i < (int)m_planes.size(); ++i) { // Render all the borders. for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); @@ -165,7 +168,7 @@ void GLGizmoMeasure::on_render() // Render features: for (const SurfaceFeature& feature : m_planes[i].surface_features) { Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - if (feature.type == SurfaceFeature::Line) { + if (m_show_edges && feature.type == SurfaceFeature::Line) { auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); view_feature_matrix *= q; view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); @@ -174,19 +177,21 @@ void GLGizmoMeasure::on_render() m_vbo_cylinder.render(); } - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); + if (m_show_points && feature.type == SurfaceFeature::Line || m_show_circles && feature.type == SurfaceFeature::Circle) { + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + } shader->set_uniform("view_model_matrix", view_model_matrix); } - if (! m_show_all) + if (! m_show_all_planes) break; } } @@ -268,7 +273,7 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) plane.surface_features.clear(); const Vec3d& normal = plane.normal; - const double edge_threshold = 10. * (M_PI/180.); + const double edge_threshold = 25. * (M_PI/180.); @@ -276,9 +281,10 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) assert(border.size() > 1); assert(! border.front().isApprox(border.back())); double last_angle = 0.; - size_t first_idx = 0; + int first_idx = 0; + bool circle = false; - for (size_t i=0; i edge_threshold || i == border.size() - 1) { + bool same_as_last = Slic3r::is_approx(angle, last_angle); + + if (std::abs(angle) > edge_threshold || (! same_as_last && circle) || i == border.size() - 1) { // Current feature ended. Save it and remember current point as beginning of the next. bool is_line = (i == first_idx + 1); plane.surface_features.emplace_back(SurfaceFeature{ @@ -300,11 +308,13 @@ void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) 0. // FIXME }); first_idx = i; - } else if (Slic3r::is_approx(angle, last_angle)) { + circle = false; + } else if (same_as_last && ! circle) { // possibly a segment of a circle - } else { + first_idx = std::max(i-2, 0); + circle = true; + } else if (! circle) { first_idx = i; - } } last_angle = angle; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 3099366736..9534796a71 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -24,7 +24,10 @@ class GLGizmoMeasure : public GLGizmoBase private: int m_currently_shown_plane = 0; - bool m_show_all = false; + bool m_show_all_planes = false; + bool m_show_points = true; + bool m_show_edges = true; + bool m_show_circles = true; GLModel m_vbo_sphere; GLModel m_vbo_cylinder; From 4c5fa7a857aa711d4988982de0336c56f31be958 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Jul 2022 16:37:16 +0200 Subject: [PATCH 193/327] Measuring: Improved feature detection, added circle center calculation --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 120 ++++++++++++++--------- 1 file changed, 75 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0cb1067c39..e00d587814 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -9,6 +9,7 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SurfaceMesh.hpp" +#include "libslic3r/Geometry/Circle.hpp" #include @@ -177,7 +178,7 @@ void GLGizmoMeasure::on_render() m_vbo_cylinder.render(); } - if (m_show_points && feature.type == SurfaceFeature::Line || m_show_circles && feature.type == SurfaceFeature::Circle) { + if ((m_show_points && feature.type == SurfaceFeature::Line) || m_show_circles && feature.type == SurfaceFeature::Circle) { view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -185,6 +186,14 @@ void GLGizmoMeasure::on_render() ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : ColorRGBA(0.f, 1.f, 0.f, 1.f)); m_vbo_sphere.render(); + + /*view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.endpoint)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line + ? ColorRGBA(1.f, 0.f, 0.f, 1.f) + : ColorRGBA(1.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render();*/ } @@ -243,7 +252,7 @@ void GLGizmoMeasure::on_render_for_picking() update_planes(); //for (int i = 0; i < (int)m_planes.size(); ++i) { int i = m_currently_shown_plane; - if (i < m_planes.size()) { + if (i < int(m_planes.size())) { for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { m_planes[i].vbos[j].set_color(picking_color_component(j)); m_planes[i].vbos[j].render(); @@ -268,60 +277,84 @@ void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) +static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) +{ + Vec2ds pts; + double z = 0.; + for (int i=start_idx; i<=end_idx; ++i) { + Vec3d pt_transformed = trafo * border[i]; + z = pt_transformed.z(); + pts.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? + + return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); +} + + + void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) { plane.surface_features.clear(); const Vec3d& normal = plane.normal; const double edge_threshold = 25. * (M_PI/180.); + std::vector angles; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); for (const std::vector& border : plane.borders) { assert(border.size() > 1); assert(! border.front().isApprox(border.back())); - double last_angle = 0.; - int first_idx = 0; - bool circle = false; + int start_idx = -1; + + // First calculate angles at all the vertices. + angles.clear(); for (int i=0; i edge_threshold || (! same_as_last && circle) || i == border.size() - 1) { - // Current feature ended. Save it and remember current point as beginning of the next. - bool is_line = (i == first_idx + 1); - plane.surface_features.emplace_back(SurfaceFeature{ - is_line ? SurfaceFeature::Line : SurfaceFeature::Circle, - is_line ? border[first_idx] : border[first_idx], // FIXME - border[i], - 0. // FIXME - }); - first_idx = i; - circle = false; - } else if (same_as_last && ! circle) { - // possibly a segment of a circle - first_idx = std::max(i-2, 0); + + bool circle = false; + std::vector> circles; + for (int i=1; i center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(SurfaceFeature{ + SurfaceFeature::Circle, + // border[start_idx], border[end_idx], + center_and_radius.first, center_and_radius.first, center_and_radius.second + }); + } std::cout << "==================== " << std::endl; @@ -352,7 +385,7 @@ void GLGizmoMeasure::update_planes() ch.merge(vol_ch); } m_planes.clear(); - const Transform3d& inst_matrix = mo->instances.front()->get_matrix(); + // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. @@ -403,13 +436,13 @@ void GLGizmoMeasure::update_planes() assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); SurfaceMesh sm(ch.its); - for (int plane_id=0; plane_id < m_planes.size(); ++plane_id) { + for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { //int plane_id = 5; { const auto& facets = m_planes[plane_id].facets; m_planes[plane_id].borders.clear(); std::vector> visited(facets.size(), {false, false, false}); - for (int face_id=0; face_id()); - Vertex_index target = sm.target(he); + std::vector& last_border = m_planes[plane_id].borders.back(); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); const Halfedge_index he_start = he; Face_index fi = he.face(); @@ -446,15 +480,15 @@ void GLGizmoMeasure::update_planes() assert(face_it != facets.end()); assert(*face_it == int(fi)); if (visited[face_it - facets.begin()][he.side()] && he != he_start) { - m_planes[plane_id].borders.back().resize(1); + last_border.resize(1); break; } visited[face_it - facets.begin()][he.side()] = true; - m_planes[plane_id].borders.back().emplace_back(sm.point(sm.source(he)).cast()); + last_border.emplace_back(sm.point(sm.source(he)).cast()); } while (he != he_start); - if (m_planes[plane_id].borders.back().size() == 1) + if (last_border.size() == 1) m_planes[plane_id].borders.pop_back(); } } @@ -468,11 +502,7 @@ void GLGizmoMeasure::update_planes() - // Let's prepare transformation of the normal vector from mesh to instance coordinates. - Geometry::Transformation t(inst_matrix); - Vec3d scaling = t.get_scaling_factor(); - t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2))); - + // Planes are finished - let's save what we calculated it from: From 9d5e9e887078c3ca149ab5194948688390c8698e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 26 Jul 2022 10:12:59 +0200 Subject: [PATCH 194/327] Measuring: Separating frontend and backend --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Measure.cpp | 323 ++++++++++++++++ src/libslic3r/Measure.hpp | 98 +++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 448 ++++------------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 43 +-- 5 files changed, 499 insertions(+), 415 deletions(-) create mode 100644 src/libslic3r/Measure.cpp create mode 100644 src/libslic3r/Measure.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ac1abe5375..a52848b229 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -180,6 +180,8 @@ set(SLIC3R_SOURCES MultiMaterialSegmentation.hpp MeshNormals.hpp MeshNormals.cpp + Measure.hpp + Measure.cpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp new file mode 100644 index 0000000000..d5cb9c24b3 --- /dev/null +++ b/src/libslic3r/Measure.cpp @@ -0,0 +1,323 @@ +#include "Measure.hpp" + +#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/SurfaceMesh.hpp" + + + +namespace Slic3r { +namespace Measure { + + + +static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) +{ + Vec2ds pts; + double z = 0.; + for (int i=start_idx; i<=end_idx; ++i) { + Vec3d pt_transformed = trafo * border[i]; + z = pt_transformed.z(); + pts.emplace_back(pt_transformed.x(), pt_transformed.y()); + } + + auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? + + return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); +} + + + + +class MeasuringImpl { +public: + explicit MeasuringImpl(const indexed_triangle_set& its); + struct PlaneData { + std::vector facets; + std::vector> borders; // FIXME: should be in fact local in update_planes() + std::vector> surface_features; + Vec3d normal; + float area; + }; + + const std::vector& get_features() const; + +private: + void update_planes(); + void extract_features(PlaneData& plane); + void save_features(); + + + std::vector m_planes; + std::vector m_features; + const indexed_triangle_set& m_its; +}; + + + + + + +MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) +: m_its{its} +{ + update_planes(); + + for (PlaneData& plane : m_planes) { + extract_features(plane); + + plane.borders.clear(); + plane.borders.shrink_to_fit(); + } + + save_features(); +} + + +void MeasuringImpl::update_planes() +{ + m_planes.clear(); + + // Now we'll go through all the facets and append Points of facets sharing the same normal. + // This part is still performed in mesh coordinate system. + const size_t num_of_facets = m_its.indices.size(); + std::vector face_to_plane(num_of_facets, size_t(-1)); + const std::vector face_normals = its_face_normals(m_its); + const std::vector face_neighbors = its_face_neighbors(m_its); + std::vector facet_queue(num_of_facets, 0); + int facet_queue_cnt = 0; + const stl_normal* normal_ptr = nullptr; + size_t seed_facet_idx = 0; + + auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { + return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); + }; + + while (1) { + // Find next unvisited triangle: + for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) + if (face_to_plane[seed_facet_idx] == size_t(-1)) { + facet_queue[facet_queue_cnt ++] = seed_facet_idx; + normal_ptr = &face_normals[seed_facet_idx]; + face_to_plane[seed_facet_idx] = m_planes.size(); + m_planes.emplace_back(); + break; + } + if (seed_facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (is_same_normal(this_normal, *normal_ptr)) { + const Vec3i& face = m_its.indices[facet_idx]; + + face_to_plane[facet_idx] = m_planes.size() - 1; + m_planes.back().facets.emplace_back(facet_idx); + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + + m_planes.back().normal = normal_ptr->cast(); + std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); + } + + assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + + SurfaceMesh sm(m_its); + for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { + //int plane_id = 5; { + const auto& facets = m_planes[plane_id].facets; + m_planes[plane_id].borders.clear(); + std::vector> visited(facets.size(), {false, false, false}); + + for (int face_id=0; face_id& last_border = m_planes[plane_id].borders.back(); + last_border.emplace_back(sm.point(sm.source(he)).cast()); + //Vertex_index target = sm.target(he); + const Halfedge_index he_start = he; + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + visited[face_it - facets.begin()][he.side()] = true; + + do { + const Halfedge_index he_orig = he; + he = sm.next_around_target(he); + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) + he = sm.next_around_target(he); + he = sm.opposite(he); + + Face_index fi = he.face(); + auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); + assert(face_it != facets.end()); + assert(*face_it == int(fi)); + if (visited[face_it - facets.begin()][he.side()] && he != he_start) { + last_border.resize(1); + break; + } + visited[face_it - facets.begin()][he.side()] = true; + + last_border.emplace_back(sm.point(sm.source(he)).cast()); + } while (he != he_start); + + if (last_border.size() == 1) + m_planes[plane_id].borders.pop_back(); + } + } + } + + m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), + [](const PlaneData& p) { return p.borders.empty(); }), + m_planes.end()); +} + + + + + + +void MeasuringImpl::extract_features(PlaneData& plane) +{ + plane.surface_features.clear(); + const Vec3d& normal = plane.normal; + + const double edge_threshold = 25. * (M_PI/180.); + std::vector angles; + + Eigen::Quaterniond q; + q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); + Transform3d trafo = Transform3d::Identity(); + trafo.rotate(q); + + + + for (const std::vector& border : plane.borders) { + assert(border.size() > 1); + int start_idx = -1; + + + // First calculate angles at all the vertices. + angles.clear(); + for (int i=0; i> circles; + for (int i=1; i circles[cidx].first) + i = circles[cidx++].second; + else plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[i-1], border[i]))); + } + + // FIXME Throw away / do not create edges which are parts of circles. + + // FIXME Check and maybe merge first and last circle. + + for (const auto& [start_idx, end_idx] : circles) { + std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(std::unique_ptr( + new Circle(center_and_radius.first, center_and_radius.second) + )); + } + + } +} + + + +void MeasuringImpl::save_features() +{ + m_features.clear(); + for (PlaneData& plane : m_planes) + //PlaneData& plane = m_planes[0]; + { + for (std::unique_ptr& feature : plane.surface_features) { + m_features.emplace_back(feature.get()); + } + } +} + + + +const std::vector& MeasuringImpl::get_features() const +{ + return m_features; +} + + + + + + + + + + + + + +Measuring::Measuring(const indexed_triangle_set& its) +: priv{std::make_unique(its)} +{} + +Measuring::~Measuring() {} + + +const std::vector& Measuring::get_features() const +{ + return priv->get_features(); +} + + + + + + +} // namespace Measure +} // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp new file mode 100644 index 0000000000..bbc7d9e1e9 --- /dev/null +++ b/src/libslic3r/Measure.hpp @@ -0,0 +1,98 @@ +#ifndef Slic3r_Measure_hpp_ +#define Slic3r_Measure_hpp_ + +#include + +#include "Point.hpp" + + +struct indexed_triangle_set; + + + +namespace Slic3r { +namespace Measure { + + +enum class SurfaceFeatureType { + Edge = 1 << 0, + Circle = 1 << 1, + Plane = 1 << 2 + }; + +class SurfaceFeature { +public: + virtual SurfaceFeatureType get_type() const = 0; +}; + +class Edge : public SurfaceFeature { +public: + Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } + std::pair get_edge() const { return std::make_pair(m_start, m_end); } +private: + Vec3d m_start; + Vec3d m_end; +}; + +class Circle : public SurfaceFeature { +public: + Circle(const Vec3d& center, double radius) : m_center{center}, m_radius{radius} {} + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } + Vec3d get_center() const { return m_center; } + double get_radius() const { return m_radius; } +private: + Vec3d m_center; + double m_radius; +}; + +class Plane : public SurfaceFeature { +public: + SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } + +}; + + +class MeasuringImpl; + + +class Measuring { +public: + // Construct the measurement object on a given its. The its must remain + // valid and unchanged during the whole lifetime of the object. + explicit Measuring(const indexed_triangle_set& its); + ~Measuring(); + + // Return a reference to a list of all features identified on the its. + const std::vector& get_features() const; + + // Given a face_idx where the mouse cursor points, return a feature that + // should be highlighted or nullptr. + const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + + // Returns distance between two SurfaceFeatures. + static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); + + // Returns true if an x/y/z distance between features makes sense. + // If so, result contains the distances. + static bool get_distances(const SurfaceFeature* a, const SurfaceFeature* b, std::array& result); + + // Returns true if an x/y/z distance between feature and a point makes sense. + // If so, result contains the distances. + static bool get_axis_aligned_distances(const SurfaceFeature* feature, const Vec3d* pt, std::array& result); + + // Returns true if measuring angles between features makes sense. + // If so, result contains the angle in radians. + static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); + + +private: + + std::unique_ptr priv; +}; + + +} // namespace Measure +} // namespace Slic3r + +#endif // Slic3r_Measure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e00d587814..043adba976 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -6,10 +6,8 @@ #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" -#include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/SurfaceMesh.hpp" -#include "libslic3r/Geometry/Circle.hpp" +#include "libslic3r/Measure.hpp" #include @@ -30,6 +28,10 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { + m_mouse_pos_x = mouse_event.GetX(); + m_mouse_pos_y = mouse_event.GetY(); + + if (mouse_event.Moving()) { // only for sure m_mouse_left_down = false; @@ -38,12 +40,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (mouse_event.LeftDown()) { if (m_hover_id != -1) { m_mouse_left_down = true; - Selection &selection = m_parent.get_selection(); - if (selection.is_single_full_instance()) { - // Rotate the object so the normal points downward: - selection.flattening_rotate(m_planes[m_hover_id].normal); - m_parent.do_rotate(L("Gizmo-Place on Face")); - } + return true; } @@ -94,7 +91,7 @@ void GLGizmoMeasure::on_set_state() CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const { - return CommonGizmosDataID::SelectionInfo; + return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::Raycaster)); } @@ -139,70 +136,59 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (this->is_plane_update_necessary()) - update_planes(); + + + update_if_needed(); + m_imgui->begin(std::string("DEBUG")); - if (m_imgui->button("<-")) - --m_currently_shown_plane; - ImGui::SameLine(); - if (m_imgui->button("->")) - ++m_currently_shown_plane; - m_currently_shown_plane = std::clamp(m_currently_shown_plane, 0, std::max(0, int(m_planes.size())-1)); - m_imgui->text(std::to_string(m_currently_shown_plane)); - m_imgui->checkbox(wxString("Show all"), m_show_all_planes); - m_imgui->checkbox(wxString("Show points"), m_show_points); - m_imgui->checkbox(wxString("Show edges"), m_show_edges); - m_imgui->checkbox(wxString("Show circles"), m_show_circles); - m_imgui->end(); + + m_imgui->checkbox(wxString("Show all features"), m_show_all); + + Vec3f pos; + Vec3f normal; + size_t facet_idx; + m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), m, camera, pos, normal, nullptr, &facet_idx); + ImGui::Separator(); + m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); + m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); + m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); + m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - int i = m_show_all_planes ? 0 : m_currently_shown_plane; - for (; i < (int)m_planes.size(); ++i) { - // Render all the borders. - for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { - m_planes[i].vbos[j].set_color(j == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - m_planes[i].vbos[j].render(); - } + + if (m_show_all) { + const std::vector features = m_measuring->get_features(); + for (const Measure::SurfaceFeature* feature : features) { + + if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { + const auto* circle = static_cast(feature); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); + } - // Render features: - for (const SurfaceFeature& feature : m_planes[i].surface_features) { - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - if (m_show_edges && feature.type == SurfaceFeature::Line) { - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), feature.endpoint - feature.pos); + else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { + const auto* edge = static_cast(feature); + auto& [start, end] = edge->get_edge(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.3, 0.3, (feature.endpoint - feature.pos).norm())); + view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); m_vbo_cylinder.render(); } - if ((m_show_points && feature.type == SurfaceFeature::Line) || m_show_circles && feature.type == SurfaceFeature::Circle) { - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.pos)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); - - /*view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.endpoint)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(feature.type == SurfaceFeature::Line - ? ColorRGBA(1.f, 0.f, 0.f, 1.f) - : ColorRGBA(1.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render();*/ - } - - shader->set_uniform("view_model_matrix", view_model_matrix); } - - if (! m_show_all_planes) - break; + shader->set_uniform("view_model_matrix", view_model_matrix); } + m_imgui->end(); } glsafe(::glEnable(GL_CULL_FACE)); @@ -222,290 +208,45 @@ void GLGizmoMeasure::on_render() #error NOT IMPLEMENTED #endif - - - - void GLGizmoMeasure::on_render_for_picking() { - const Selection& selection = m_parent.get_selection(); - - GLShaderProgram* shader = wxGetApp().get_shader("flat"); - if (shader == nullptr) - return; - - shader->start_using(); - - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glDisable(GL_BLEND)); - glsafe(::glLineWidth(2.f)); - - if (selection.is_single_full_instance() && !wxGetKeyState(WXK_CONTROL)) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; - - shader->set_uniform("view_model_matrix", view_model_matrix); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - if (this->is_plane_update_necessary()) - update_planes(); - //for (int i = 0; i < (int)m_planes.size(); ++i) { - int i = m_currently_shown_plane; - if (i < int(m_planes.size())) { - for (int j=0; j<(int)m_planes[i].vbos.size(); ++j) { - m_planes[i].vbos[j].set_color(picking_color_component(j)); - m_planes[i].vbos[j].render(); - } - } - } - - glsafe(::glEnable(GL_CULL_FACE)); - - shader->stop_using(); } void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) { - if (model_object != m_old_model_object) { - m_planes.clear(); - m_planes_valid = false; - } + if (model_object != m_old_model_object) + update_if_needed(); } - -static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) -{ - Vec2ds pts; - double z = 0.; - for (int i=start_idx; i<=end_idx; ++i) { - Vec3d pt_transformed = trafo * border[i]; - z = pt_transformed.z(); - pts.emplace_back(pt_transformed.x(), pt_transformed.y()); - } - - auto circle = Geometry::circle_ransac(pts, 20); // FIXME: iterations? - - return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); -} - - - -void GLGizmoMeasure::extract_features(GLGizmoMeasure::PlaneData& plane) -{ - plane.surface_features.clear(); - const Vec3d& normal = plane.normal; - - const double edge_threshold = 25. * (M_PI/180.); - std::vector angles; - - Eigen::Quaterniond q; - q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); - Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); - - - - for (const std::vector& border : plane.borders) { - assert(border.size() > 1); - assert(! border.front().isApprox(border.back())); - int start_idx = -1; - - - // First calculate angles at all the vertices. - angles.clear(); - for (int i=0; i> circles; - for (int i=1; i center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(SurfaceFeature{ - SurfaceFeature::Circle, - // border[start_idx], border[end_idx], - center_and_radius.first, center_and_radius.first, center_and_radius.second - }); - } - - - std::cout << "==================== " << std::endl; - } - - - for (const SurfaceFeature& f : plane.surface_features) { - std::cout << "- detected " << (f.type == SurfaceFeature::Line ? "Line" : "Circle") << std::endl; - std::cout<< f.pos << std::endl << std::endl << f.endpoint << std::endl; - std::cout << "----------------" << std::endl; - } - - - -} - - - -void GLGizmoMeasure::update_planes() +void GLGizmoMeasure::update_if_needed() { const ModelObject* mo = m_c->selection_info()->model_object(); - TriangleMesh ch; - for (const ModelVolume* vol : mo->volumes) { - if (vol->type() != ModelVolumeType::MODEL_PART) - continue; - TriangleMesh vol_ch = vol->mesh(); - vol_ch.transform(vol->get_matrix()); - ch.merge(vol_ch); - } - m_planes.clear(); - + if (m_state != On || ! mo || mo->instances.empty()) + return; - // Now we'll go through all the facets and append Points of facets sharing the same normal. - // This part is still performed in mesh coordinate system. - const size_t num_of_facets = ch.facets_count(); - std::vector face_to_plane(num_of_facets, size_t(-1)); - const std::vector face_normals = its_face_normals(ch.its); - const std::vector face_neighbors = its_face_neighbors(ch.its); - std::vector facet_queue(num_of_facets, 0); - int facet_queue_cnt = 0; - const stl_normal* normal_ptr = nullptr; - size_t seed_facet_idx = 0; + if (! m_measuring || mo != m_old_model_object + || mo->volumes.size() != m_volumes_matrices.size()) + goto UPDATE; - auto is_same_normal = [](const stl_normal& a, const stl_normal& b) -> bool { - return (std::abs(a(0) - b(0)) < 0.001 && std::abs(a(1) - b(1)) < 0.001 && std::abs(a(2) - b(2)) < 0.001); - }; + // We want to recalculate when the scale changes - some planes could (dis)appear. + if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) + || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + goto UPDATE; - while (1) { - // Find next unvisited triangle: - for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) - if (face_to_plane[seed_facet_idx] == size_t(-1)) { - facet_queue[facet_queue_cnt ++] = seed_facet_idx; - normal_ptr = &face_normals[seed_facet_idx]; - face_to_plane[seed_facet_idx] = m_planes.size(); - m_planes.emplace_back(); - break; - } - if (seed_facet_idx == num_of_facets) - break; // Everything was visited already + for (unsigned int i=0; i < mo->volumes.size(); ++i) + if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) + || mo->volumes[i]->type() != m_volumes_types[i]) + goto UPDATE; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = face_normals[facet_idx]; - if (is_same_normal(this_normal, *normal_ptr)) { - const Vec3i& face = ch.its.indices[facet_idx]; + return; - face_to_plane[facet_idx] = m_planes.size() - 1; - m_planes.back().facets.emplace_back(facet_idx); - for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } +UPDATE: + m_measuring.reset(new Measure::Measuring(mo->volumes.front()->mesh().its)); - m_planes.back().normal = normal_ptr->cast(); - std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); - } - - assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); - - SurfaceMesh sm(ch.its); - for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { - //int plane_id = 5; { - const auto& facets = m_planes[plane_id].facets; - m_planes[plane_id].borders.clear(); - std::vector> visited(facets.size(), {false, false, false}); - - for (int face_id=0; face_id& last_border = m_planes[plane_id].borders.back(); - last_border.emplace_back(sm.point(sm.source(he)).cast()); - //Vertex_index target = sm.target(he); - const Halfedge_index he_start = he; - - Face_index fi = he.face(); - auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); - assert(face_it != facets.end()); - assert(*face_it == int(fi)); - visited[face_it - facets.begin()][he.side()] = true; - - do { - const Halfedge_index he_orig = he; - he = sm.next_around_target(he); - while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) - he = sm.next_around_target(he); - he = sm.opposite(he); - - Face_index fi = he.face(); - auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi)); - assert(face_it != facets.end()); - assert(*face_it == int(fi)); - if (visited[face_it - facets.begin()][he.side()] && he != he_start) { - last_border.resize(1); - break; - } - visited[face_it - facets.begin()][he.side()] = true; - - last_border.emplace_back(sm.point(sm.source(he)).cast()); - } while (he != he_start); - - if (last_border.size() == 1) - m_planes[plane_id].borders.pop_back(); - } - } - } - - - // DEBUGGING: - m_planes.erase(std::remove_if(m_planes.begin(), m_planes.end(), [](const PlaneData& p) { return p.borders.empty(); }), m_planes.end()); - - - - - - - - - // Planes are finished - let's save what we calculated it from: + // Let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); for (const ModelVolume* vol : mo->volumes) { @@ -515,65 +256,6 @@ void GLGizmoMeasure::update_planes() m_first_instance_scale = mo->instances.front()->get_scaling_factor(); m_first_instance_mirror = mo->instances.front()->get_mirror(); m_old_model_object = mo; - - // And finally create respective VBOs. The polygon is convex with - // the vertices in order, so triangulation is trivial. - for (PlaneData& plane : m_planes) { - for (std::vector& vertices : plane.borders) { - GLModel::Geometry init_data; - init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.reserve_vertices(vertices.size()); - init_data.reserve_indices(vertices.size()); - // vertices + indices - for (size_t i = 0; i < vertices.size(); ++i) { - init_data.add_vertex((Vec3f)vertices[i].cast(), (Vec3f)plane.normal.cast()); - init_data.add_index((unsigned int)i); - } - plane.vbos.emplace_back(); - plane.vbos.back().init_from(std::move(init_data)); - vertices.pop_back(); // first and last are the same - } - - static int n=0; - std::cout << "==================== " << std::endl; - std::cout << "==================== " << std::endl; - std::cout << "==================== " << std::endl; - std::cout << "Plane num. " << n++ << std::endl; - extract_features(plane); - - - // FIXME: vertices should really be local, they need not - // persist now when we use VBOs - plane.borders.clear(); - plane.borders.shrink_to_fit(); - } - - m_planes_valid = true; -} - - - -bool GLGizmoMeasure::is_plane_update_necessary() const -{ - const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state != On || ! mo || mo->instances.empty()) - return false; - - if (! m_planes_valid || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) - return true; - - // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) - return true; - - for (unsigned int i=0; i < mo->volumes.size(); ++i) - if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || mo->volumes[i]->type() != m_volumes_types[i]) - return true; - - return false; } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9534796a71..2781d2b353 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -9,10 +9,15 @@ #endif // ENABLE_LEGACY_OPENGL_REMOVAL +#include + + namespace Slic3r { enum class ModelVolumeType : int; +namespace Measure { class Measuring; } + namespace GUI { @@ -22,53 +27,27 @@ class GLGizmoMeasure : public GLGizmoBase // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. private: - - int m_currently_shown_plane = 0; - bool m_show_all_planes = false; - bool m_show_points = true; - bool m_show_edges = true; - bool m_show_circles = true; + std::unique_ptr m_measuring; GLModel m_vbo_sphere; GLModel m_vbo_cylinder; - struct SurfaceFeature { - enum Type { - Circle, - Line - }; - Type type; - Vec3d pos; - Vec3d endpoint; // for type == Line - double radius; // for type == Circle; - }; - - struct PlaneData { - std::vector facets; - std::vector> borders; // should be in fact local in update_planes() - std::vector surface_features; - std::vector vbos; - Vec3d normal; - float area; - }; - - static void extract_features(PlaneData& plane); - // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; Vec3d m_first_instance_scale; Vec3d m_first_instance_mirror; - std::vector m_planes; - std::vector m_face_to_plane; bool m_mouse_left_down = false; // for detection left_up of this gizmo bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; std::vector instances_matrices; - void update_planes(); - bool is_plane_update_necessary() const; + int m_mouse_pos_x; + int m_mouse_pos_y; + bool m_show_all = true; + + void update_if_needed(); void set_flattening_data(const ModelObject* model_object); public: From 7d6d33f92c9becccc942d0bccd662164b9a1830f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 09:58:21 +0200 Subject: [PATCH 195/327] Measuring: further separating frontend and backend --- src/libslic3r/Measure.cpp | 236 +++++++++++++++-------- src/libslic3r/Measure.hpp | 24 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 91 ++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 4 files changed, 233 insertions(+), 122 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index d5cb9c24b3..a8ee3d3265 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -39,16 +39,19 @@ public: float area; }; - const std::vector& get_features() const; + const std::vector& get_features() const; + const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + const std::vector> get_planes_triangle_indices() const; private: void update_planes(); - void extract_features(PlaneData& plane); + void extract_features(); void save_features(); std::vector m_planes; - std::vector m_features; + std::vector m_face_to_plane; + std::vector m_features; const indexed_triangle_set& m_its; }; @@ -61,14 +64,7 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) : m_its{its} { update_planes(); - - for (PlaneData& plane : m_planes) { - extract_features(plane); - - plane.borders.clear(); - plane.borders.shrink_to_fit(); - } - + extract_features(); save_features(); } @@ -80,7 +76,7 @@ void MeasuringImpl::update_planes() // Now we'll go through all the facets and append Points of facets sharing the same normal. // This part is still performed in mesh coordinate system. const size_t num_of_facets = m_its.indices.size(); - std::vector face_to_plane(num_of_facets, size_t(-1)); + m_face_to_plane.resize(num_of_facets, size_t(-1)); const std::vector face_normals = its_face_normals(m_its); const std::vector face_neighbors = its_face_neighbors(m_its); std::vector facet_queue(num_of_facets, 0); @@ -95,10 +91,10 @@ void MeasuringImpl::update_planes() while (1) { // Find next unvisited triangle: for (; seed_facet_idx < num_of_facets; ++ seed_facet_idx) - if (face_to_plane[seed_facet_idx] == size_t(-1)) { + if (m_face_to_plane[seed_facet_idx] == size_t(-1)) { facet_queue[facet_queue_cnt ++] = seed_facet_idx; normal_ptr = &face_normals[seed_facet_idx]; - face_to_plane[seed_facet_idx] = m_planes.size(); + m_face_to_plane[seed_facet_idx] = m_planes.size(); m_planes.emplace_back(); break; } @@ -111,10 +107,10 @@ void MeasuringImpl::update_planes() if (is_same_normal(this_normal, *normal_ptr)) { const Vec3i& face = m_its.indices[facet_idx]; - face_to_plane[facet_idx] = m_planes.size() - 1; + m_face_to_plane[facet_idx] = m_planes.size() - 1; m_planes.back().facets.emplace_back(facet_idx); for (int j = 0; j < 3; ++ j) - if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && face_to_plane[neighbor_idx] == size_t(-1)) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && m_face_to_plane[neighbor_idx] == size_t(-1)) facet_queue[facet_queue_cnt ++] = neighbor_idx; } } @@ -123,7 +119,7 @@ void MeasuringImpl::update_planes() std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end()); } - assert(std::none_of(face_to_plane.begin(), face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); + assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); })); SurfaceMesh sm(m_its); for (int plane_id=0; plane_id < int(m_planes.size()); ++plane_id) { @@ -133,9 +129,9 @@ void MeasuringImpl::update_planes() std::vector> visited(facets.size(), {false, false, false}); for (int face_id=0; face_id angles; - Eigen::Quaterniond q; - q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); - Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); - - - for (const std::vector& border : plane.borders) { - assert(border.size() > 1); - int start_idx = -1; + for (int i=0; i& border : plane.borders) { + assert(border.size() > 1); + int start_idx = -1; + + // First calculate angles at all the vertices. + angles.clear(); + for (int i=0; i> circles; - for (int i=1; i> circles; + for (int i=1; i circles[cidx].first) + i = circles[cidx++].second; + else plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[i-1], border[i]))); + } + + // FIXME Throw away / do not create edges which are parts of circles or + // which lead to circle points (unless they belong to the same plane.) + + // FIXME Check and merge first and last circle if needed. + + // Now create the circle-typed surface features. + for (const auto& [start_idx, end_idx] : circles) { + std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); + plane.surface_features.emplace_back(std::unique_ptr( + new Circle(center_and_radius.first, center_and_radius.second))); + } + } - // We have the circles. Now go around again and pick edges. - int cidx = 0; // index of next circle in the way - for (int i=1; i circles[cidx].first) - i = circles[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); - } - - // FIXME Throw away / do not create edges which are parts of circles. - - // FIXME Check and maybe merge first and last circle. - - for (const auto& [start_idx, end_idx] : circles) { - std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second) - )); - } + // The last surface feature is the plane itself. + plane.surface_features.emplace_back(std::unique_ptr( + new Plane(i))); + plane.borders.clear(); + plane.borders.shrink_to_fit(); } } @@ -277,7 +284,7 @@ void MeasuringImpl::save_features() for (PlaneData& plane : m_planes) //PlaneData& plane = m_planes[0]; { - for (std::unique_ptr& feature : plane.surface_features) { + for (const std::unique_ptr& feature : plane.surface_features) { m_features.emplace_back(feature.get()); } } @@ -285,13 +292,51 @@ void MeasuringImpl::save_features() -const std::vector& MeasuringImpl::get_features() const +const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const +{ + if (face_idx >= m_face_to_plane.size()) + return nullptr; + + const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; + + const SurfaceFeature* closest_feature = nullptr; + double min_dist = std::numeric_limits::max(); + + for (const std::unique_ptr& feature : plane.surface_features) { + double dist = Measuring::get_distance(feature.get(), &point); + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = feature.get(); + } + } + + if (closest_feature) + return closest_feature; + + // Nothing detected, return the plane as a whole. + assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); + return plane.surface_features.back().get(); +} + + + +const std::vector& MeasuringImpl::get_features() const { return m_features; } +const std::vector> MeasuringImpl::get_planes_triangle_indices() const +{ + std::vector> out; + for (const PlaneData& plane : m_planes) + out.emplace_back(plane.facets); + return out; +} + + + @@ -309,12 +354,39 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +const std::vector& Measuring::get_features() const { return priv->get_features(); } +const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +{ + return priv->get_feature(face_idx, point); +} + + + +const std::vector> Measuring::get_planes_triangle_indices() const +{ + return priv->get_planes_triangle_indices(); +} + + + +double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) +{ + if (feature->get_type() == SurfaceFeatureType::Edge) { + const Edge* edge = static_cast(feature); + const auto& [s,e] = edge->get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + return line.distance(*pt); + } + + return std::numeric_limits::max(); +} + + diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index bbc7d9e1e9..1360b47ff7 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -48,8 +48,12 @@ private: class Plane : public SurfaceFeature { public: + Plane(int idx) : m_idx(idx) {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } + int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices +private: + int m_idx; }; @@ -64,30 +68,30 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - const std::vector& get_features() const; + [[deprecated]]const std::vector& get_features() const; // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted or nullptr. const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // Returns a list of triangle indices for each identified plane. Each + // Plane object contains an index into this vector. + const std::vector> get_planes_triangle_indices() const; + + + // Returns distance between two SurfaceFeatures. static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - // Returns true if an x/y/z distance between features makes sense. - // If so, result contains the distances. - static bool get_distances(const SurfaceFeature* a, const SurfaceFeature* b, std::array& result); - - // Returns true if an x/y/z distance between feature and a point makes sense. - // If so, result contains the distances. - static bool get_axis_aligned_distances(const SurfaceFeature* feature, const Vec3d* pt, std::array& result); + // Returns distance between a SurfaceFeature and a point. + static double get_distance(const SurfaceFeature* a, const Vec3d* pt); // Returns true if measuring angles between features makes sense. // If so, result contains the angle in radians. static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); -private: - +private: std::unique_ptr priv; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 043adba976..a8b257a937 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -144,6 +144,7 @@ void GLGizmoMeasure::on_render() m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); + m_imgui->checkbox(wxString("Show all planes"), m_show_planes); Vec3f pos; Vec3f normal; @@ -157,37 +158,51 @@ void GLGizmoMeasure::on_render() - if (m_show_all) { - const std::vector features = m_measuring->get_features(); - for (const Measure::SurfaceFeature* feature : features) { + std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + if (m_show_all) { + features = m_measuring->get_features(); + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature* f) { + return f->get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); + } + + + for (const Measure::SurfaceFeature* feature : features) { + if (! feature) + continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); - m_vbo_sphere.render(); - } - - - else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); - shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_cylinder.set_color(ColorRGBA(0.7f, 0.7f, 0.f, 1.f)); - m_vbo_cylinder.render(); - } - - + if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { + const auto* circle = static_cast(feature); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.render(); } - shader->set_uniform("view_model_matrix", view_model_matrix); + else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { + const auto* edge = static_cast(feature); + auto& [start, end] = edge->get_edge(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + view_feature_matrix *= q; + view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_cylinder.render(); + } + else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { + const auto* plane = static_cast(feature); + assert(plane->get_plane_idx() < m_plane_models.size()); + m_plane_models[plane->get_plane_idx()]->render(); + } } + shader->set_uniform("view_model_matrix", view_model_matrix); + if (m_show_planes) + for (const auto& glmodel : m_plane_models) + glmodel->render(); + m_imgui->end(); } @@ -244,7 +259,25 @@ void GLGizmoMeasure::update_if_needed() return; UPDATE: - m_measuring.reset(new Measure::Measuring(mo->volumes.front()->mesh().its)); + const indexed_triangle_set& its = mo->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + m_plane_models.clear(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + for (const std::vector& triangle_indices : planes_triangles) { + m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); + int i = 0; + for (int idx : triangle_indices) { + init_data.add_vertex(its.vertices[its.indices[idx][0]]); + init_data.add_vertex(its.vertices[its.indices[idx][1]]); + init_data.add_vertex(its.vertices[its.indices[idx][2]]); + init_data.add_triangle(i, i+1, i+2); + i+=3; + } + m_plane_models.back()->init_from(std::move(init_data)); + } // Let's save what we calculated it from: m_volumes_matrices.clear(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 2781d2b353..f74b82fa46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -45,7 +45,9 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; - bool m_show_all = true; + bool m_show_all = false; + bool m_show_planes = false; + std::vector> m_plane_models; void update_if_needed(); void set_flattening_data(const ModelObject* model_object); From 457afca5de3359703df88105acc7600a3da9e505 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 11:45:42 +0200 Subject: [PATCH 196/327] Measuring: added getters for circle visualization --- src/libslic3r/Measure.cpp | 11 ++++++++++- src/libslic3r/Measure.hpp | 8 +++++++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 22 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index a8ee3d3265..724f4ab806 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -262,7 +262,7 @@ void MeasuringImpl::extract_features() for (const auto& [start_idx, end_idx] : circles) { std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second))); + new Circle(center_and_radius.first, center_and_radius.second, plane.normal))); } } @@ -382,6 +382,15 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) Eigen::ParametrizedLine line(s, (e-s).normalized()); return line.distance(*pt); } + else if (feature->get_type() == SurfaceFeatureType::Circle) { + const Circle* circle = static_cast(feature); + // Find a plane containing normal, center and the point. + const Vec3d& c = circle->get_center(); + const Vec3d& n = circle->get_normal(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(*pt); + return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + } return std::numeric_limits::max(); } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1360b47ff7..0455291bfc 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -28,22 +28,28 @@ public: class Edge : public SurfaceFeature { public: Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} + Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, + m_pin{std::unique_ptr(new Vec3d(pin))} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } std::pair get_edge() const { return std::make_pair(m_start, m_end); } private: Vec3d m_start; Vec3d m_end; + std::unique_ptr m_pin; }; class Circle : public SurfaceFeature { public: - Circle(const Vec3d& center, double radius) : m_center{center}, m_radius{radius} {} + Circle(const Vec3d& center, double radius, const Vec3d& normal) + : m_center{center}, m_radius{radius}, m_normal{normal} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } Vec3d get_center() const { return m_center; } double get_radius() const { return m_radius; } + Vec3d get_normal() const { return m_normal; } private: Vec3d m_center; double m_radius; + Vec3d m_normal; }; class Plane : public SurfaceFeature { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a8b257a937..44dd44cc55 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -174,12 +174,28 @@ void GLGizmoMeasure::on_render() if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { const auto* circle = static_cast(feature); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(circle->get_center())); + const Vec3d& c = circle->get_center(); + const Vec3d& n = circle->get_normal(); + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.f, 1.f, 0.f, 1.f)); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_sphere.render(); + + // Now draw the circle itself - let's take a funny shortcut: + Vec3d rad = n.cross(Vec3d::UnitX()); + if (rad.squaredNorm() < 0.1) + rad = n.cross(Vec3d::UnitY()); + rad *= circle->get_radius() * rad.norm(); + const int N = 20; + for (int i=0; iset_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.render(); + } } else if (feature->get_type() == Measure::SurfaceFeatureType::Edge) { const auto* edge = static_cast(feature); From 387dfb2b793bf8399d354c240cc65f819ec0dc54 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 27 Jul 2022 15:36:21 +0200 Subject: [PATCH 197/327] Measuring: Add detection of polygons and their centers --- src/libslic3r/Measure.cpp | 72 ++++++++++++++++++------ src/libslic3r/Measure.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 8 +++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 724f4ab806..056178bc4e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -192,10 +192,11 @@ void MeasuringImpl::update_planes() void MeasuringImpl::extract_features() { - - - const double edge_threshold = 25. * (M_PI/180.); + auto N_to_angle = [](double N) -> double { return 2.*M_PI / N; }; + constexpr double polygon_upper_threshold = N_to_angle(4.5); + constexpr double polygon_lower_threshold = N_to_angle(8.5); std::vector angles; + std::vector lengths; for (int i=0; i M_PI) + angle = 2*M_PI - angle; + angles.push_back(angle); + lengths.push_back(v2.squaredNorm()); } assert(border.size() == angles.size()); + assert(border.size() == lengths.size()); bool circle = false; - std::vector> circles; + std::vector> circles; + std::vector> circles_idxs; for (int i=1; i( + new Circle(center, radius, plane.normal))); circle = false; } } } + // Some of the "circles" may actually be polygons. We want them detected as + // edges, but also to remember the center and save it into those edges. + // We will add all such edges manually and delete the detected circles, + // leaving it in circles_idxs so they are not picked again: + assert(circles.size() == circles_idxs.size()); + for (int i=circles.size()-1; i>=0; --i) { + assert(circles_idxs[i].first + 1 < angles.size() - 1); // Check that this is internal point of the circle, not the first, not the last. + double angle = angles[circles_idxs[i].first + 1]; + if (angle > polygon_lower_threshold) { + if (angle < polygon_upper_threshold) { + const Vec3d center = static_cast(circles[i].get())->get_center(); + for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) + plane.surface_features.emplace_back(std::unique_ptr( + new Edge(border[j-1], border[j], center))); + } else { + // This will be handled just like a regular edge. + circles_idxs.erase(circles_idxs.begin() + i); + } + circles.erase(circles.begin() + i); + } + } + + + + + + // We have the circles. Now go around again and pick edges. int cidx = 0; // index of next circle in the way for (int i=1; i circles[cidx].first) - i = circles[cidx++].second; + if (cidx < circles_idxs.size() && i > circles_idxs[cidx].first) + i = circles_idxs[cidx++].second; else plane.surface_features.emplace_back(std::unique_ptr( new Edge(border[i-1], border[i]))); } @@ -258,13 +297,10 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. - // Now create the circle-typed surface features. - for (const auto& [start_idx, end_idx] : circles) { - std::pair center_and_radius = get_center_and_radius(border, start_idx, end_idx, trafo); - plane.surface_features.emplace_back(std::unique_ptr( - new Circle(center_and_radius.first, center_and_radius.second, plane.normal))); - } - + // Now move the circles into the feature list. + assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), + std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 0455291bfc..1db35d9fcd 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -32,6 +32,7 @@ public: m_pin{std::unique_ptr(new Vec3d(pin))} {} SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } std::pair get_edge() const { return std::make_pair(m_start, m_end); } + const Vec3d* get_point_of_interest() const { return m_pin.get(); } private: Vec3d m_start; Vec3d m_end; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 44dd44cc55..1869983015 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -207,6 +207,14 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); + if (edge->get_point_of_interest()) { + Vec3d pin = *edge->get_point_of_interest(); + view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.render(); + } } else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { const auto* plane = static_cast(feature); From b23e28e9e4d7faff8b3f920ab47aa79c2a2a4965 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Aug 2022 13:42:26 +0200 Subject: [PATCH 198/327] Measuring: refactoring --- src/libslic3r/Measure.cpp | 166 ++++++++++++++--------- src/libslic3r/Measure.hpp | 114 ++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 45 +++--- 3 files changed, 180 insertions(+), 145 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 056178bc4e..e59e3c04d4 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -34,24 +34,21 @@ public: struct PlaneData { std::vector facets; std::vector> borders; // FIXME: should be in fact local in update_planes() - std::vector> surface_features; + std::vector surface_features; Vec3d normal; float area; }; - const std::vector& get_features() const; - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; - const std::vector> get_planes_triangle_indices() const; + std::vector get_all_features() const; + std::optional get_feature(size_t face_idx, const Vec3d& point) const; + std::vector> get_planes_triangle_indices() const; private: void update_planes(); void extract_features(); - void save_features(); - - + std::vector m_planes; std::vector m_face_to_plane; - std::vector m_features; const indexed_triangle_set& m_its; }; @@ -65,7 +62,6 @@ MeasuringImpl::MeasuringImpl(const indexed_triangle_set& its) { update_planes(); extract_features(); - save_features(); } @@ -233,7 +229,7 @@ void MeasuringImpl::extract_features() bool circle = false; - std::vector> circles; + std::vector circles; std::vector> circles_idxs; for (int i=1; i( - new Circle(center, radius, plane.normal))); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); circle = false; } } @@ -266,10 +261,10 @@ void MeasuringImpl::extract_features() double angle = angles[circles_idxs[i].first + 1]; if (angle > polygon_lower_threshold) { if (angle < polygon_upper_threshold) { - const Vec3d center = static_cast(circles[i].get())->get_center(); + const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) - plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[j-1], border[j], center))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, + border[j-1], border[j], std::make_optional(center), 0.)); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +283,8 @@ void MeasuringImpl::extract_features() for (int i=1; i circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(std::unique_ptr( - new Edge(border[i-1], border[i]))); + else plane.surface_features.emplace_back(SurfaceFeature( + SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); } // FIXME Throw away / do not create edges which are parts of circles or @@ -298,14 +293,16 @@ void MeasuringImpl::extract_features() // FIXME Check and merge first and last circle if needed. // Now move the circles into the feature list. - assert(std::all_of(circles.begin(), circles.end(), [](const std::unique_ptr& f) { return f->get_type() == SurfaceFeatureType::Circle; })); + assert(std::all_of(circles.begin(), circles.end(), [](const SurfaceFeature& f) { + return f.get_type() == SurfaceFeatureType::Circle; + })); plane.surface_features.insert(plane.surface_features.end(), std::make_move_iterator(circles.begin()), std::make_move_iterator(circles.end())); } // The last surface feature is the plane itself. - plane.surface_features.emplace_back(std::unique_ptr( - new Plane(i))); + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, + Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -314,56 +311,58 @@ void MeasuringImpl::extract_features() -void MeasuringImpl::save_features() +std::vector MeasuringImpl::get_all_features() const { - m_features.clear(); - for (PlaneData& plane : m_planes) + std::vector features; //PlaneData& plane = m_planes[0]; - { - for (const std::unique_ptr& feature : plane.surface_features) { - m_features.emplace_back(feature.get()); - } - } + for (const PlaneData& plane : m_planes) + for (const SurfaceFeature& feature : plane.surface_features) + features.emplace_back(feature); + return features; } -const SurfaceFeature* MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const + + + +std::optional MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point) const { if (face_idx >= m_face_to_plane.size()) - return nullptr; + return std::optional(); const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; const SurfaceFeature* closest_feature = nullptr; double min_dist = std::numeric_limits::max(); - for (const std::unique_ptr& feature : plane.surface_features) { - double dist = Measuring::get_distance(feature.get(), &point); - if (dist < 0.5 && dist < min_dist) { - min_dist = std::min(dist, min_dist); - closest_feature = feature.get(); + MeasurementResult res; + SurfaceFeature point_sf(point); + + for (const SurfaceFeature& feature : plane.surface_features) { + res = get_measurement(feature, point_sf); + if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. + double dist = *res.distance_strict; + if (dist < 0.5 && dist < min_dist) { + min_dist = std::min(dist, min_dist); + closest_feature = &feature; + } } } if (closest_feature) - return closest_feature; + return std::make_optional(*closest_feature); // Nothing detected, return the plane as a whole. - assert(plane.surface_features.back().get()->get_type() == SurfaceFeatureType::Plane); - return plane.surface_features.back().get(); + assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane); + return std::make_optional(plane.surface_features.back()); } -const std::vector& MeasuringImpl::get_features() const -{ - return m_features; -} - -const std::vector> MeasuringImpl::get_planes_triangle_indices() const +std::vector> MeasuringImpl::get_planes_triangle_indices() const { std::vector> out; for (const PlaneData& plane : m_planes) @@ -390,45 +389,81 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -const std::vector& Measuring::get_features() const +std::vector Measuring::get_all_features() const { - return priv->get_features(); + return priv->get_all_features(); } -const SurfaceFeature* Measuring::get_feature(size_t face_idx, const Vec3d& point) const +std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const { return priv->get_feature(face_idx, point); } -const std::vector> Measuring::get_planes_triangle_indices() const +std::vector> Measuring::get_planes_triangle_indices() const { return priv->get_planes_triangle_indices(); } -double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + + + + + + + + + +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) { - if (feature->get_type() == SurfaceFeatureType::Edge) { - const Edge* edge = static_cast(feature); - const auto& [s,e] = edge->get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - return line.distance(*pt); - } - else if (feature->get_type() == SurfaceFeatureType::Circle) { - const Circle* circle = static_cast(feature); - // Find a plane containing normal, center and the point. - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); - Eigen::Hyperplane circle_plane(n, c); - Vec3d proj = circle_plane.projection(*pt); - return std::sqrt( std::pow((proj - c).norm() - circle->get_radius(), 2.) + (*pt - proj).squaredNorm()); + assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); + + const bool swap = int(a.get_type()) > int(b.get_type()); + const SurfaceFeature& f1 = swap ? b : a; + const SurfaceFeature& f2 = swap ? a : b; + + MeasurementResult result; + if (f1.get_type() == SurfaceFeatureType::Point) { + if (f2.get_type() == SurfaceFeatureType::Point) { + Vec3d diff = (f2.get_point() - f1.get_point()); + result.distance_strict = diff.norm(); + result.distance_xyz = diff; + } else if (f2.get_type() == SurfaceFeatureType::Edge) { + const auto& [s,e] = f2.get_edge(); + Eigen::ParametrizedLine line(s, (e-s).normalized()); + result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + // Find a plane containing normal, center and the point. + const auto& [c, radius, n] = f2.get_circle(); + Eigen::Hyperplane circle_plane(n, c); + Vec3d proj = circle_plane.projection(f1.get_point()); + result.distance_strict = std::make_optional(std::sqrt( + std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Edge) { + if (f2.get_type() == SurfaceFeatureType::Edge) { + } else if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Circle) { + if (f2.get_type() == SurfaceFeatureType::Circle) { + } else if (f2.get_type() == SurfaceFeatureType::Plane) { + } + } else if (f1.get_type() == SurfaceFeatureType::Plane) { + assert(f2.get_type() == SurfaceFeatureType::Plane); + } - return std::numeric_limits::max(); + + + return result; } @@ -436,5 +471,8 @@ double Measuring::get_distance(const SurfaceFeature* feature, const Vec3d* pt) + + + } // namespace Measure } // namespace Slic3r diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1db35d9fcd..87bed1cf17 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,7 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#include #include #include "Point.hpp" @@ -15,55 +16,50 @@ namespace Measure { enum class SurfaceFeatureType { - Edge = 1 << 0, - Circle = 1 << 1, - Plane = 1 << 2 - }; + Undef, + Point, + Edge, + Circle, + Plane +}; class SurfaceFeature { -public: - virtual SurfaceFeatureType get_type() const = 0; -}; - -class Edge : public SurfaceFeature { public: - Edge(const Vec3d& start, const Vec3d& end) : m_start{start}, m_end{end} {} - Edge(const Vec3d& start, const Vec3d& end, const Vec3d& pin) : m_start{start}, m_end{end}, - m_pin{std::unique_ptr(new Vec3d(pin))} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Edge; } - std::pair get_edge() const { return std::make_pair(m_start, m_end); } - const Vec3d* get_point_of_interest() const { return m_pin.get(); } -private: - Vec3d m_start; - Vec3d m_end; - std::unique_ptr m_pin; -}; + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) + : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} -class Circle : public SurfaceFeature { -public: - Circle(const Vec3d& center, double radius, const Vec3d& normal) - : m_center{center}, m_radius{radius}, m_normal{normal} {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Circle; } - Vec3d get_center() const { return m_center; } - double get_radius() const { return m_radius; } - Vec3d get_normal() const { return m_normal; } -private: - Vec3d m_center; - double m_radius; - Vec3d m_normal; -}; + explicit SurfaceFeature(const Vec3d& pt) + : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} -class Plane : public SurfaceFeature { -public: - Plane(int idx) : m_idx(idx) {} - SurfaceFeatureType get_type() const override { return SurfaceFeatureType::Plane; } - int get_plane_idx() const { return m_idx; } // index into vector provided by Measuring::get_plane_triangle_indices + + // Get type of this feature. + SurfaceFeatureType get_type() const { return m_type; } + + // For points, return the point. + Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; } + + // For edges, return start and end. + std::pair get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); } + + // For circles, return center, radius and normal. + std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } + + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. + int get_plane_idx() const { return int(m_value); } + + // For anything, return an extra point that should also be considered a part of this. + std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } private: - int m_idx; + SurfaceFeatureType m_type = SurfaceFeatureType::Undef; + Vec3d m_pt1; + Vec3d m_pt2; + std::optional m_pt3; + double m_value; }; + class MeasuringImpl; @@ -75,34 +71,36 @@ public: ~Measuring(); // Return a reference to a list of all features identified on the its. - [[deprecated]]const std::vector& get_features() const; + // Use only for debugging. Expensive, do not call often. + [[deprecated]] std::vector get_all_features() const; // Given a face_idx where the mouse cursor points, return a feature that - // should be highlighted or nullptr. - const SurfaceFeature* get_feature(size_t face_idx, const Vec3d& point) const; + // should be highlighted (if any). + std::optional get_feature(size_t face_idx, const Vec3d& point) const; // Returns a list of triangle indices for each identified plane. Each - // Plane object contains an index into this vector. - const std::vector> get_planes_triangle_indices() const; - - - - // Returns distance between two SurfaceFeatures. - static double get_distance(const SurfaceFeature* a, const SurfaceFeature* b); - - // Returns distance between a SurfaceFeature and a point. - static double get_distance(const SurfaceFeature* a, const Vec3d* pt); - - // Returns true if measuring angles between features makes sense. - // If so, result contains the angle in radians. - static bool get_angle(const SurfaceFeature* a, const SurfaceFeature* b, double& result); - - + // Plane object contains an index into this vector. Expensive, do not + // call too often. + std::vector> get_planes_triangle_indices() const; + private: std::unique_ptr priv; }; + + +struct MeasurementResult { + std::optional angle; + std::optional distance_infinite; + std::optional distance_strict; + std::optional distance_xyz; +}; + +// Returns distance/angle between two SurfaceFeatures. +static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); + + } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 1869983015..7eaf7b2e0b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -158,24 +158,25 @@ void GLGizmoMeasure::on_render() - std::vector features = {m_measuring->get_feature(facet_idx, pos.cast())}; + std::vector features; if (m_show_all) { - features = m_measuring->get_features(); + features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature* f) { - return f->get_type() == Measure::SurfaceFeatureType::Plane; + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); + } else { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat) + features.emplace_back(*feat); } - - - for (const Measure::SurfaceFeature* feature : features) { - if (! feature) - continue; - if (feature->get_type() == Measure::SurfaceFeatureType::Circle) { - const auto* circle = static_cast(feature); - const Vec3d& c = circle->get_center(); - const Vec3d& n = circle->get_normal(); + + + for (const Measure::SurfaceFeature& feature : features) { + + if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -186,7 +187,7 @@ void GLGizmoMeasure::on_render() Vec3d rad = n.cross(Vec3d::UnitX()); if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); - rad *= circle->get_radius() * rad.norm(); + rad *= radius * rad.norm(); const int N = 20; for (int i=0; iget_type() == Measure::SurfaceFeatureType::Edge) { - const auto* edge = static_cast(feature); - auto& [start, end] = edge->get_edge(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + const auto& [start, end] = feature.get_edge(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); view_feature_matrix *= q; @@ -207,8 +207,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_feature_matrix); m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); m_vbo_cylinder.render(); - if (edge->get_point_of_interest()) { - Vec3d pin = *edge->get_point_of_interest(); + if (feature.get_extra_point()) { + Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); @@ -216,10 +216,9 @@ void GLGizmoMeasure::on_render() m_vbo_sphere.render(); } } - else if (feature->get_type() == Measure::SurfaceFeatureType::Plane) { - const auto* plane = static_cast(feature); - assert(plane->get_plane_idx() < m_plane_models.size()); - m_plane_models[plane->get_plane_idx()]->render(); + else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + assert(feature.get_plane_idx() < m_plane_models.size()); + m_plane_models[feature.get_plane_idx()]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix); From b646fcad95b1b0726f1c2fc8c5dd4c8cf5aa7f32 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 18 Aug 2022 18:50:49 +0200 Subject: [PATCH 199/327] Measuring: implemented edge endpoint detection --- src/libslic3r/Measure.cpp | 91 ++++++++++++++++++++---- src/libslic3r/Measure.hpp | 9 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 14 +++- 3 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index e59e3c04d4..8c7254f85b 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -9,6 +9,10 @@ namespace Slic3r { namespace Measure { +constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it +constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ... + + static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) { @@ -302,7 +306,7 @@ void MeasuringImpl::extract_features() // The last surface feature is the plane itself. plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - Vec3d::Zero(), Vec3d::Zero(), std::optional(), i + 0.0001)); + plane.normal, plane.borders.front().front(), std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -333,25 +337,39 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const const PlaneData& plane = m_planes[m_face_to_plane[face_idx]]; - const SurfaceFeature* closest_feature = nullptr; + size_t closest_feature_idx = size_t(-1); double min_dist = std::numeric_limits::max(); MeasurementResult res; SurfaceFeature point_sf(point); - for (const SurfaceFeature& feature : plane.surface_features) { - res = get_measurement(feature, point_sf); + for (size_t i=0; i line(s, (e-s).normalized()); - result.distance_strict = std::make_optional(line.distance(f1.get_point())); // TODO: this is really infinite dist + double dist_inf = line.distance(f1.get_point()); + Vec3d proj = line.projection(f1.get_point()); + double len_sq = (e-s).squaredNorm(); + double dist_start_sq = (proj-s).squaredNorm(); + double dist_end_sq = (proj-e).squaredNorm(); + if (dist_start_sq < len_sq && dist_end_sq < len_sq) { + // projection falls on the line - the strict distance is the same as infinite + result.distance_strict = std::make_optional(dist_inf); + } else { // the result is the closer of the endpoints + result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + } + result.distance_infinite = std::make_optional(dist_inf); + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. const auto& [c, radius, n] = f2.get_circle(); Eigen::Hyperplane circle_plane(n, c); Vec3d proj = circle_plane.projection(f1.get_point()); - result.distance_strict = std::make_optional(std::sqrt( - std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm())); + double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + + (f1.get_point() - proj).squaredNorm()); + + result.distance_strict = std::make_optional(dist); + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + const auto& [idx, normal, pt] = f2.get_plane(); + Eigen::Hyperplane plane(normal, pt); + result.distance_infinite = plane.absDistance(f1.get_point()); + // TODO: result.distance_strict = } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { } + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Plane) { assert(f2.get_type() == SurfaceFeatureType::Plane); - + + const auto& [idx1, normal1, pt1] = f1.get_plane(); + const auto& [idx2, normal2, pt2] = f2.get_plane(); + double angle = 0.; + + if (! normal1.isApprox(normal2)) { + // The planes are parallel, calculate distance. + Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = plane.absDistance(pt2); + } else { + // Planes are not parallel, calculate angle. + angle = std::acos(std::abs(normal1.dot(normal2))); + } + result.angle = angle; } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 87bed1cf17..fe0b1687a1 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -31,7 +31,6 @@ public: explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} - // Get type of this feature. SurfaceFeatureType get_type() const { return m_type; } @@ -44,8 +43,8 @@ public: // For circles, return center, radius and normal. std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } - // For planes, return index into vector provided by Measuring::get_plane_triangle_indices. - int get_plane_idx() const { return int(m_value); } + // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. + std::tuple get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); } // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } @@ -84,7 +83,7 @@ public: std::vector> get_planes_triangle_indices() const; private: - std::unique_ptr priv; + std::unique_ptr priv; }; @@ -98,7 +97,7 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -static MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); } // namespace Measure diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 7eaf7b2e0b..07c920120b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -175,7 +175,14 @@ void GLGizmoMeasure::on_render() for (const Measure::SurfaceFeature& feature : features) { - if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { + if (feature.get_type() == Measure::SurfaceFeatureType::Point) { + Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); + view_feature_matrix.scale(0.5); + shader->set_uniform("view_model_matrix", view_feature_matrix); + m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.render(); + } + else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { const auto& [c, radius, n] = feature.get_circle(); Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); @@ -217,8 +224,9 @@ void GLGizmoMeasure::on_render() } } else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { - assert(feature.get_plane_idx() < m_plane_models.size()); - m_plane_models[feature.get_plane_idx()]->render(); + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models.size()); + m_plane_models[idx]->render(); } } shader->set_uniform("view_model_matrix", view_model_matrix); From 1942932229d28a62fe4985736734a14d0498c4a1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 09:05:44 +0200 Subject: [PATCH 200/327] Partial revert of 1e494e30 --- src/libslic3r/TriangleMesh.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index ef96e2400f..df820fac92 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -885,20 +885,13 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor indexed_triangle_set its_make_cube(double xd, double yd, double zd) { auto x = float(xd), y = float(yd), z = float(zd); - /*return { + return { { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5} }, { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - };*/ - return { - { {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, - {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, - {2, 5, 6}, {2, 5, 3}, {4, 0, 3}, /*{4, 3, 5}*/ }, - { {x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, - {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z} } - }; + }; } indexed_triangle_set its_make_prism(float width, float length, float height) From 964fa581fa30fe8584dd234f943d5b7bdefc993c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 09:19:10 +0200 Subject: [PATCH 201/327] Use unified color for hovering in GLGizmoMeasure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 07c920120b..c17a116cc5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,8 +16,7 @@ namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.9f }; -static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.2f, 0.2f, 1.f }; +static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) @@ -126,7 +125,10 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); - glsafe(::glLineWidth(2.f)); +#if ENABLE_GL_CORE_PROFILE + if (!OpenGLManager::get_gl_info().is_core_profile()) +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.f)); if (selection.is_single_full_instance()) { const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); @@ -179,7 +181,7 @@ void GLGizmoMeasure::on_render() Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { @@ -187,7 +189,7 @@ void GLGizmoMeasure::on_render() Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); // Now draw the circle itself - let's take a funny shortcut: @@ -212,20 +214,21 @@ void GLGizmoMeasure::on_render() view_feature_matrix *= q; view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_cylinder.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_cylinder.set_color(HOVER_COLOR); m_vbo_cylinder.render(); if (feature.get_extra_point()) { Vec3d pin = *feature.get_extra_point(); view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); view_feature_matrix.scale(0.5); shader->set_uniform("view_model_matrix", view_feature_matrix); - m_vbo_sphere.set_color(ColorRGBA(0.8f, 0.2f, 0.2f, 1.f)); + m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } } else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models.size()); + m_plane_models[idx]->set_color(HOVER_COLOR); m_plane_models[idx]->render(); } } From b821f1a33eab088b0cd7a3a5efbd78dc82226d5b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 10:36:28 +0200 Subject: [PATCH 202/327] New icon for Measure Gizmo --- resources/icons/measure.svg | 106 +++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/resources/icons/measure.svg b/resources/icons/measure.svg index 275c522251..3ea137a1ed 100644 --- a/resources/icons/measure.svg +++ b/resources/icons/measure.svg @@ -1,13 +1,93 @@ - - - Layer 1 - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + From a74c070a5e407d547da98dc4b5cebbe314c7ed15 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 10:48:32 +0200 Subject: [PATCH 203/327] Set Measure Gizmo to be activable for single volume selections only --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 11 ++--------- src/slic3r/GUI/Selection.hpp | 1 + 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c17a116cc5..582c76a0da 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -100,17 +100,12 @@ std::string GLGizmoMeasure::on_get_name() const return _u8L("Measure"); } - - bool GLGizmoMeasure::on_is_activable() const { - // 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(); + const Selection& selection = m_parent.get_selection(); + return selection.is_single_volume() || selection.is_single_volume_instance(); } - - void GLGizmoMeasure::on_render() { const Selection& selection = m_parent.get_selection(); @@ -139,9 +134,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - update_if_needed(); - m_imgui->begin(std::string("DEBUG")); diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index cebea29e05..e118f6bca5 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -328,6 +328,7 @@ public: #if ENABLE_WORLD_COORDINATE bool is_single_volume_or_modifier() const { return is_single_volume() || is_single_modifier(); } #endif // ENABLE_WORLD_COORDINATE + bool is_single_volume_instance() const { return is_single_full_instance() && m_list.size() == 1; } bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); } // returns true if the selection contains all the given indices From a99871a2ab46ce3828ba2ffcb43309dc4cde6482 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 19 Aug 2022 11:02:13 +0200 Subject: [PATCH 204/327] Removed method set_flattening_data() from GLGizmoMeasure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 ++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 582c76a0da..c9ec63acdc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -69,7 +69,8 @@ void GLGizmoMeasure::data_changed() selection.is_from_single_object() ) { model_object = selection.get_model()->objects[selection.get_object_idx()]; } - set_flattening_data(model_object); + if (model_object != m_old_model_object) + update_if_needed(); } @@ -256,12 +257,6 @@ void GLGizmoMeasure::on_render_for_picking() -void GLGizmoMeasure::set_flattening_data(const ModelObject* model_object) -{ - if (model_object != m_old_model_object) - update_if_needed(); -} - void GLGizmoMeasure::update_if_needed() { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index f74b82fa46..bc48adea1b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -50,7 +50,6 @@ private: std::vector> m_plane_models; void update_if_needed(); - void set_flattening_data(const ModelObject* model_object); public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); @@ -63,6 +62,7 @@ public: bool on_mouse(const wxMouseEvent &mouse_event) override; void data_changed() override; + protected: bool on_init() override; std::string on_get_name() const override; From 6a2e7930cf64cb1bd12e5bd0afcd2867fad18649 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 22 Aug 2022 08:52:02 +0200 Subject: [PATCH 205/327] Measuring: allow to select single parts of a multipart object while Gizmo Measure is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 136 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 26 +++-- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 6 +- 4 files changed, 105 insertions(+), 70 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c9ec63acdc..3e37fad743 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -64,12 +64,14 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) void GLGizmoMeasure::data_changed() { const Selection & selection = m_parent.get_selection(); - const ModelObject *model_object = nullptr; + const ModelObject* model_object = nullptr; + const ModelVolume* model_volume = nullptr; if (selection.is_single_full_instance() || selection.is_from_single_object() ) { model_object = selection.get_model()->objects[selection.get_object_idx()]; - } - if (model_object != m_old_model_object) + model_volume = model_object->volumes[selection.get_first_volume()->volume_idx()]; + } + if (model_object != m_old_model_object || model_volume != m_old_model_volume) update_if_needed(); } @@ -109,6 +111,10 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { + // do not render if the user is panning/rotating the 3d scene + if (m_parent.is_mouse_dragging()) + return; + const Selection& selection = m_parent.get_selection(); GLShaderProgram* shader = wxGetApp().get_shader("flat"); @@ -126,33 +132,34 @@ void GLGizmoMeasure::on_render() #endif // ENABLE_GL_CORE_PROFILE glsafe(::glLineWidth(2.f)); - if (selection.is_single_full_instance()) { - const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); + if (selection.is_single_volume() || selection.is_single_volume_instance()) { + const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; + Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); update_if_needed(); - m_imgui->begin(std::string("DEBUG")); - - m_imgui->checkbox(wxString("Show all features"), m_show_all); - m_imgui->checkbox(wxString("Show all planes"), m_show_planes); - Vec3f pos; Vec3f normal; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), m, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); + +#define ENABLE_DEBUG_DIALOG 0 +#if ENABLE_DEBUG_DIALOG + m_imgui->begin(std::string("DEBUG")); + m_imgui->checkbox(wxString("Show all features"), m_show_all); + m_imgui->checkbox(wxString("Show all planes"), m_show_planes); ImGui::Separator(); m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - - + m_imgui->end(); +#endif // ENABLE_DEBUG_DIALOG std::vector features; if (m_show_all) { @@ -230,8 +237,6 @@ void GLGizmoMeasure::on_render() if (m_show_planes) for (const auto& glmodel : m_plane_models) glmodel->render(); - - m_imgui->end(); } glsafe(::glEnable(GL_CULL_FACE)); @@ -260,57 +265,70 @@ void GLGizmoMeasure::on_render_for_picking() void GLGizmoMeasure::update_if_needed() { + auto do_update = [this](const ModelObject* object, const ModelVolume* volume) { + const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + m_plane_models.clear(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + for (const std::vector& triangle_indices : planes_triangles) { + m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + GUI::GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); + int i = 0; + for (int idx : triangle_indices) { + init_data.add_vertex(its.vertices[its.indices[idx][0]]); + init_data.add_vertex(its.vertices[its.indices[idx][1]]); + init_data.add_vertex(its.vertices[its.indices[idx][2]]); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + m_plane_models.back()->init_from(std::move(init_data)); + } + + // Let's save what we calculated it from: + m_volumes_matrices.clear(); + m_volumes_types.clear(); + m_first_instance_scale = Vec3d::Ones(); + m_first_instance_mirror = Vec3d::Ones(); + if (object != nullptr) { + for (const ModelVolume* vol : object->volumes) { + m_volumes_matrices.push_back(vol->get_matrix()); + m_volumes_types.push_back(vol->type()); + } + m_first_instance_scale = object->instances.front()->get_scaling_factor(); + m_first_instance_mirror = object->instances.front()->get_mirror(); + } + m_old_model_object = object; + m_old_model_volume = volume; + }; + const ModelObject* mo = m_c->selection_info()->model_object(); - if (m_state != On || ! mo || mo->instances.empty()) + const ModelVolume* mv = m_c->selection_info()->model_volume(); + if (m_state != On || (mo == nullptr && mv == nullptr)) return; - if (! m_measuring || mo != m_old_model_object - || mo->volumes.size() != m_volumes_matrices.size()) - goto UPDATE; + if (mo == nullptr) + mo = mv->get_object(); + + if (mo->instances.empty()) + return; + + if (!m_measuring || mo != m_old_model_object || mv != m_old_model_volume || mo->volumes.size() != m_volumes_matrices.size()) + do_update(mo, mv); // We want to recalculate when the scale changes - some planes could (dis)appear. - if (! mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) - || ! mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) - goto UPDATE; + if (!mo->instances.front()->get_scaling_factor().isApprox(m_first_instance_scale) || + !mo->instances.front()->get_mirror().isApprox(m_first_instance_mirror)) + do_update(mo, mv); - for (unsigned int i=0; i < mo->volumes.size(); ++i) - if (! mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) - || mo->volumes[i]->type() != m_volumes_types[i]) - goto UPDATE; - - return; - -UPDATE: - const indexed_triangle_set& its = mo->volumes.front()->mesh().its; - m_measuring.reset(new Measure::Measuring(its)); - m_plane_models.clear(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - for (const std::vector& triangle_indices : planes_triangles) { - m_plane_models.emplace_back(std::unique_ptr(new GLModel())); - GUI::GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; - init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); - int i = 0; - for (int idx : triangle_indices) { - init_data.add_vertex(its.vertices[its.indices[idx][0]]); - init_data.add_vertex(its.vertices[its.indices[idx][1]]); - init_data.add_vertex(its.vertices[its.indices[idx][2]]); - init_data.add_triangle(i, i+1, i+2); - i+=3; + for (unsigned int i = 0; i < mo->volumes.size(); ++i) { + if (!mo->volumes[i]->get_matrix().isApprox(m_volumes_matrices[i]) || + mo->volumes[i]->type() != m_volumes_types[i]) { + do_update(mo, mv); + break; } - m_plane_models.back()->init_from(std::move(init_data)); } - - // Let's save what we calculated it from: - m_volumes_matrices.clear(); - m_volumes_types.clear(); - for (const ModelVolume* vol : mo->volumes) { - m_volumes_matrices.push_back(vol->get_matrix()); - m_volumes_types.push_back(vol->type()); - } - m_first_instance_scale = mo->instances.front()->get_scaling_factor(); - m_first_instance_mirror = mo->instances.front()->get_mirror(); - m_old_model_object = mo; } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index bc48adea1b..68baffd4ce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -14,6 +14,8 @@ namespace Slic3r { +class ModelVolume; + enum class ModelVolumeType : int; namespace Measure { class Measuring; } @@ -35,12 +37,13 @@ private: // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; - Vec3d m_first_instance_scale; - Vec3d m_first_instance_mirror; + Vec3d m_first_instance_scale{ Vec3d::Ones() }; + Vec3d m_first_instance_mirror{ Vec3d::Ones() }; bool m_mouse_left_down = false; // for detection left_up of this gizmo bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; + const ModelVolume* m_old_model_volume = nullptr; std::vector instances_matrices; int m_mouse_pos_x; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index ce3ba1d4ab..1eef6aced5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -119,21 +119,25 @@ void SelectionInfo::on_update() const Selection& selection = get_pool()->get_canvas()->get_selection(); if (selection.is_single_full_instance()) { m_model_object = selection.get_model()->objects[selection.get_object_idx()]; + m_model_volume = nullptr; m_z_shift = selection.get_first_volume()->get_sla_shift_z(); } - else + else { m_model_object = nullptr; + if (selection.is_single_volume()) + m_model_volume = selection.get_model()->objects[selection.get_object_idx()]->volumes[selection.get_first_volume()->volume_idx()]; + } } void SelectionInfo::on_release() { m_model_object = nullptr; + m_model_volume = nullptr; } int SelectionInfo::get_active_instance() const { - const Selection& selection = get_pool()->get_canvas()->get_selection(); - return selection.get_instance_idx(); + return get_pool()->get_canvas()->get_selection().get_instance_idx(); } @@ -329,12 +333,18 @@ void Raycaster::on_update() { wxBusyCursor wait; const ModelObject* mo = get_pool()->selection_info()->model_object(); + const ModelVolume* mv = get_pool()->selection_info()->model_volume(); - if (! mo) + if (mo == nullptr && mv == nullptr) return; + std::vector mvs; + if (mv != nullptr) + mvs.push_back(const_cast(mv)); + else + mvs = mo->volumes; + std::vector meshes; - const std::vector& mvs = mo->volumes; if (mvs.size() == 1) { assert(mvs.front()->is_model_part()); const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh(); @@ -342,9 +352,9 @@ void Raycaster::on_update() meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh()); } if (meshes.empty()) { - for (const ModelVolume* mv : mvs) { - if (mv->is_model_part()) - meshes.push_back(&mv->mesh()); + for (const ModelVolume* v : mvs) { + if (v->is_model_part()) + meshes.push_back(&v->mesh()); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 7dd2c110ea..c8b29f761e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -10,7 +10,7 @@ namespace Slic3r { class ModelObject; - +class ModelVolume; namespace GUI { @@ -153,7 +153,10 @@ public: explicit SelectionInfo(CommonGizmosDataPool* cgdp) : CommonGizmosDataBase(cgdp) {} + // Returns a non-null pointer if the selection is a single full instance ModelObject* model_object() const { return m_model_object; } + // Returns a non-null pointer if the selection is a single volume + ModelVolume* model_volume() const { return m_model_volume; } int get_active_instance() const; float get_sla_shift() const { return m_z_shift; } @@ -163,6 +166,7 @@ protected: private: ModelObject* m_model_object = nullptr; + ModelVolume* m_model_volume = nullptr; // int m_active_inst = -1; float m_z_shift = 0.f; }; From adb3d0101deefbdd51a3c7185ed8f768f2852efc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Aug 2022 13:17:09 +0200 Subject: [PATCH 206/327] Measuring: reworked rendering of hovered features --- src/slic3r/GUI/GLModel.cpp | 104 +++++++++++++++ src/slic3r/GUI/GLModel.hpp | 7 ++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 154 ++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 + 4 files changed, 211 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 2d6a66d43c..4c98b05172 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2253,6 +2253,110 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) return data; } + +GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) +{ + resolution = std::max(4, resolution); + + const unsigned int sectorCount = resolution; + const float sectorStep = 2.0f * float(M_PI) / float(sectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(sectorCount * 4 + 2); + data.reserve_indices(sectorCount * 4 * 3); + + auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { + std::vector ret; + ret.reserve(sectorCount); + for (unsigned int i = 0; i < sectorCount; ++i) { + // from 0 to 2pi + const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; + const float x1 = radius * std::cos(sectorAngle); + const float x2 = radius * std::sin(sectorAngle); + + Vec3f v; + switch (axis) + { + case X: { v = Vec3f(0.0f, x1, x2); break; } + case Y: { v = Vec3f(-x1, 0.0f, x2); break; } + case Z: { v = Vec3f(x1, x2, 0.0f); break; } + default: { assert(false); break; } + } + + ret.emplace_back(v); + } + return ret; + }; + + const std::vector base_vertices = generate_vertices_on_circle(axis, radius); + + Vec3f h; + switch (axis) + { + case X: { h = height * Vec3f::UnitX(); break; } + case Y: { h = height * Vec3f::UnitY(); break; } + case Z: { h = height * Vec3f::UnitZ(); break; } + default: { assert(false); break; } + } + + // stem vertices + for (unsigned int i = 0; i < sectorCount; ++i) { + const Vec3f& v = base_vertices[i]; + const Vec3f n = v.normalized(); + data.add_vertex(v, n); + data.add_vertex(v + h, n); + } + + // stem triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + unsigned int v1 = i * 2; + unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0; + unsigned int v3 = v2 + 1; + unsigned int v4 = v1 + 1; + data.add_triangle(v1, v2, v3); + data.add_triangle(v1, v3, v4); + } + + // bottom cap vertices + Vec3f cap_center = Vec3f::Zero(); + unsigned int cap_center_id = data.vertices_count(); + Vec3f normal; + switch (axis) + { + case X: { normal = -Vec3f::UnitX(); break; } + case Y: { normal = -Vec3f::UnitY(); break; } + case Z: { normal = -Vec3f::UnitZ(); break; } + default: { assert(false); break; } + } + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i], normal); + } + + // bottom cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1); + } + + // top cap vertices + cap_center += h; + cap_center_id = data.vertices_count(); + normal = -normal; + + data.add_vertex(cap_center, normal); + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_vertex(base_vertices[i] + h, normal); + } + + // top cap triangles + for (unsigned int i = 0; i < sectorCount; ++i) { + data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1); + } + + return data; +} #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index c2845c8850..393e545ab9 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -376,7 +376,14 @@ namespace GUI { #if ENABLE_LEGACY_OPENGL_REMOVAL // create a sphere with the given resolution and smooth normals // the origin of the sphere is in its center + // the radius of the sphere is the given value GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); + // create a cylinder with the given resolution and smooth normals + // the axis of the cylinder is the given value + // the radius of the cylinder is the given value + // the height of the cylinder is the given value + // the origin of the cylinder is in the center of the cap face having axis == 0 + GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e37fad743..5b6baa0153 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -8,6 +8,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/Measure.hpp" +#include "libslic3r/PresetBundle.hpp" #include @@ -21,8 +22,8 @@ static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_sphere.init_from(its_make_sphere(1., M_PI/32.)); - m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); + m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); + m_vbo_cylinder.init_from(smooth_cylinder(Z, 16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -106,7 +107,9 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - return selection.is_single_volume() || selection.is_single_volume_instance(); + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + selection.is_single_full_instance() : + selection.is_single_volume() || selection.is_single_volume_instance(); } void GLGizmoMeasure::on_render() @@ -117,30 +120,27 @@ void GLGizmoMeasure::on_render() const Selection& selection = m_parent.get_selection(); - GLShaderProgram* shader = wxGetApp().get_shader("flat"); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); + shader->set_uniform("emission_factor", 0.25f); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_BLEND)); -#if ENABLE_GL_CORE_PROFILE - if (!OpenGLManager::get_gl_info().is_core_profile()) -#endif // ENABLE_GL_CORE_PROFILE - glsafe(::glLineWidth(2.f)); - if (selection.is_single_volume() || selection.is_single_volume_instance()) { + if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && selection.is_single_full_instance()) || + (selection.is_single_volume() || selection.is_single_volume_instance())) { const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * model_matrix; + const Transform3d& view_matrix = camera.get_view_matrix(); + const float inv_zoom = camera.get_inv_zoom(); - shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - + update_if_needed(); Vec3f pos; @@ -148,7 +148,6 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#define ENABLE_DEBUG_DIALOG 0 #if ENABLE_DEBUG_DIALOG m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); @@ -162,34 +161,46 @@ void GLGizmoMeasure::on_render() #endif // ENABLE_DEBUG_DIALOG std::vector features; - if (m_show_all) { +#if ENABLE_DEBUG_DIALOG + if (m_show_all) { features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), [](const Measure::SurfaceFeature& f) { return f.get_type() == Measure::SurfaceFeatureType::Plane; }), features.end()); - } else { + } + else { +#endif // ENABLE_DEBUG_DIALOG std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat) + if (feat.has_value()) features.emplace_back(*feat); - } - - +#if ENABLE_DEBUG_DIALOG + } +#endif // ENABLE_DEBUG_DIALOG for (const Measure::SurfaceFeature& feature : features) { - - if (feature.get_type() == Measure::SurfaceFeatureType::Point) { - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(feature.get_point())); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Circle) { - const auto& [c, radius, n] = feature.get_circle(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(c)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + // render center + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); @@ -198,45 +209,69 @@ void GLGizmoMeasure::on_render() if (rad.squaredNorm() < 0.1) rad = n.cross(Vec3d::UnitY()); rad *= radius * rad.norm(); - const int N = 20; - for (int i=0; iset_uniform("view_model_matrix", view_feature_matrix); + const int N = 32; + m_vbo_sphere.set_color(HOVER_COLOR); + for (int i = 0; i < N; ++i) { + rad = Eigen::AngleAxisd(2.0 * M_PI / N, n) * rad; + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center + rad) * Geometry::scale_transform(N / 100.0 * inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.render(); } + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Edge) { + case Measure::SurfaceFeatureType::Edge: + { const auto& [start, end] = feature.get_edge(); - Transform3d view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(start)); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - view_feature_matrix *= q; - view_feature_matrix.scale(Vec3d(0.075, 0.075, (end - start).norm())); - shader->set_uniform("view_model_matrix", view_feature_matrix); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_cylinder.set_color(HOVER_COLOR); m_vbo_cylinder.render(); - if (feature.get_extra_point()) { - Vec3d pin = *feature.get_extra_point(); - view_feature_matrix = view_model_matrix * Transform3d(Eigen::Translation3d(pin)); - view_feature_matrix.scale(0.5); - shader->set_uniform("view_model_matrix", view_feature_matrix); + +/* + std::optional extra_point = feature.get_extra_point(); + if (extra_point.has_value()) { + const Vec3d pin = *extra_point; + const Transform3d feature_matrix = Geometry::translation_transform(pin + selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * + Geometry::scale_transform(inv_zoom) * model_matrix; + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_vbo_sphere.set_color(HOVER_COLOR); m_vbo_sphere.render(); } +*/ + break; } - else if (feature.get_type() == Measure::SurfaceFeatureType::Plane) { + case Measure::SurfaceFeatureType::Plane: + { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models.size()); + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); m_plane_models[idx]->set_color(HOVER_COLOR); m_plane_models[idx]->render(); - } + break; + } + } } - shader->set_uniform("view_model_matrix", view_model_matrix); +#if ENABLE_DEBUG_DIALOG if (m_show_planes) - for (const auto& glmodel : m_plane_models) + for (const auto& glmodel : m_plane_models) { + glmodel->set_color(HOVER_COLOR); glmodel->render(); + } +#endif // ENABLE_DEBUG_DIALOG } glsafe(::glEnable(GL_CULL_FACE)); @@ -272,14 +307,19 @@ void GLGizmoMeasure::update_if_needed() const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); for (const std::vector& triangle_indices : planes_triangles) { m_plane_models.emplace_back(std::unique_ptr(new GLModel())); - GUI::GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3 }; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); int i = 0; for (int idx : triangle_indices) { - init_data.add_vertex(its.vertices[its.indices[idx][0]]); - init_data.add_vertex(its.vertices[its.indices[idx][1]]); - init_data.add_vertex(its.vertices[its.indices[idx][2]]); + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); init_data.add_triangle(i, i + 1, i + 2); i += 3; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 68baffd4ce..8231711710 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -11,6 +11,7 @@ #include +#define ENABLE_DEBUG_DIALOG 0 namespace Slic3r { @@ -48,8 +49,10 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; +#if ENABLE_DEBUG_DIALOG bool m_show_all = false; bool m_show_planes = false; +#endif // ENABLE_DEBUG_DIALOG std::vector> m_plane_models; void update_if_needed(); From 74d32277039fa0070232b6d0bc5e4383f5d71fbb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 23 Aug 2022 13:53:55 +0200 Subject: [PATCH 207/327] Follow-up of a7d1c9b5e99132e51d84fcd5155ede7da1ecadd0 - Simplified code to generate a smooth cylinder --- src/slic3r/GUI/GLModel.cpp | 39 ++++-------------------- src/slic3r/GUI/GLModel.hpp | 13 +++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 4c98b05172..bb8d0b338d 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2254,7 +2254,7 @@ GLModel::Geometry smooth_sphere(unsigned int resolution, float radius) return data; } -GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height) +GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height) { resolution = std::max(4, resolution); @@ -2266,39 +2266,19 @@ GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radi data.reserve_vertices(sectorCount * 4 + 2); data.reserve_indices(sectorCount * 4 * 3); - auto generate_vertices_on_circle = [sectorCount, sectorStep](Axis axis, float radius) { + auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) { std::vector ret; ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; - const float x1 = radius * std::cos(sectorAngle); - const float x2 = radius * std::sin(sectorAngle); - - Vec3f v; - switch (axis) - { - case X: { v = Vec3f(0.0f, x1, x2); break; } - case Y: { v = Vec3f(-x1, 0.0f, x2); break; } - case Z: { v = Vec3f(x1, x2, 0.0f); break; } - default: { assert(false); break; } - } - - ret.emplace_back(v); + ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); } return ret; }; - const std::vector base_vertices = generate_vertices_on_circle(axis, radius); - - Vec3f h; - switch (axis) - { - case X: { h = height * Vec3f::UnitX(); break; } - case Y: { h = height * Vec3f::UnitY(); break; } - case Z: { h = height * Vec3f::UnitZ(); break; } - default: { assert(false); break; } - } + const std::vector base_vertices = generate_vertices_on_circle(radius); + const Vec3f h = height * Vec3f::UnitZ(); // stem vertices for (unsigned int i = 0; i < sectorCount; ++i) { @@ -2321,14 +2301,7 @@ GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radi // bottom cap vertices Vec3f cap_center = Vec3f::Zero(); unsigned int cap_center_id = data.vertices_count(); - Vec3f normal; - switch (axis) - { - case X: { normal = -Vec3f::UnitX(); break; } - case Y: { normal = -Vec3f::UnitY(); break; } - case Z: { normal = -Vec3f::UnitZ(); break; } - default: { assert(false); break; } - } + Vec3f normal = -Vec3f::UnitZ(); data.add_vertex(cap_center, normal); for (unsigned int i = 0; i < sectorCount; ++i) { diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 393e545ab9..8fe821a3da 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -374,16 +374,13 @@ namespace GUI { GLModel::Geometry diamond(unsigned int resolution); #if ENABLE_LEGACY_OPENGL_REMOVAL - // create a sphere with the given resolution and smooth normals + // create a sphere with smooth normals // the origin of the sphere is in its center - // the radius of the sphere is the given value GLModel::Geometry smooth_sphere(unsigned int resolution, float radius); - // create a cylinder with the given resolution and smooth normals - // the axis of the cylinder is the given value - // the radius of the cylinder is the given value - // the height of the cylinder is the given value - // the origin of the cylinder is in the center of the cap face having axis == 0 - GLModel::Geometry smooth_cylinder(Axis axis, unsigned int resolution, float radius, float height); + // create a cylinder with smooth normals + // the axis of the cylinder is the Z axis + // the origin of the cylinder is the center of its bottom cap face + GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5b6baa0153..327a60a046 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -23,7 +23,7 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen : GLGizmoBase(parent, icon_filename, sprite_id) { m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); - m_vbo_cylinder.init_from(smooth_cylinder(Z, 16, 5.0f, 1.0f)); + m_vbo_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) From c30cc15f41e52dbe7ff1e03c58652d8e81052e96 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 11:25:15 +0200 Subject: [PATCH 208/327] Measuring: circle feature rendered using a torus --- src/slic3r/GUI/GLModel.cpp | 47 ++++++++++++++++- src/slic3r/GUI/GLModel.hpp | 4 ++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 67 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 +- 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index bb8d0b338d..03d2677bea 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2271,7 +2271,7 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h ret.reserve(sectorCount); for (unsigned int i = 0; i < sectorCount; ++i) { // from 0 to 2pi - const float sectorAngle = (i != sectorCount) ? sectorStep * i : 0.0f; + const float sectorAngle = sectorStep * i; ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f); } return ret; @@ -2330,6 +2330,51 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h return data; } + +GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness) +{ + primary_resolution = std::max(4, primary_resolution); + secondary_resolution = std::max(4, secondary_resolution); + const unsigned int torusSectorCount = primary_resolution; + const float torusSectorStep = 2.0f * float(M_PI) / float(torusSectorCount); + const unsigned int sectionSectorCount = secondary_resolution; + const float sectionSectorStep = 2.0f * float(M_PI) / float(sectionSectorCount); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torusSectorCount * sectionSectorCount); + data.reserve_indices(torusSectorCount * sectionSectorCount * 2 * 3); + + // vertices + for (unsigned int i = 0; i < torusSectorCount; ++i) { + const float sectionAngle = torusSectorStep * i; + const Vec3f sectionCenter(radius * std::cos(sectionAngle), radius * std::sin(sectionAngle), 0.0f); + for (unsigned int j = 0; j < sectionSectorCount; ++j) { + const float circleAngle = sectionSectorStep * j; + const float thickness_xy = thickness * std::cos(circleAngle); + const float thickness_z = thickness * std::sin(circleAngle); + const Vec3f v(thickness_xy * std::cos(sectionAngle), thickness_xy * std::sin(sectionAngle), thickness_z); + data.add_vertex(sectionCenter + v, (Vec3f)v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torusSectorCount; ++i) { + const unsigned int ii = i * sectionSectorCount; + const unsigned int ii_next = ((i + 1) % torusSectorCount) * sectionSectorCount; + for (unsigned int j = 0; j < sectionSectorCount; ++j) { + const unsigned int j_next = (j + 1) % sectionSectorCount; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } + } + + return data; +} #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 8fe821a3da..e631952a3e 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -381,6 +381,10 @@ namespace GUI { // the axis of the cylinder is the Z axis // the origin of the cylinder is the center of its bottom cap face GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height); + // create a torus with smooth normals + // the axis of the torus is the Z axis + // the origin of the torus is in its center + GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 327a60a046..4e7cd3db1a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -22,8 +22,8 @@ static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_vbo_sphere.init_from(smooth_sphere(16, 7.5f)); - m_vbo_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); + m_sphere.init_from(smooth_sphere(16, 7.5f)); + m_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -188,38 +188,32 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); + m_sphere.set_color(HOVER_COLOR); + m_sphere.render(); break; } case Measure::SurfaceFeatureType::Circle: { const auto& [center, radius, n] = feature.get_circle(); // render center - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d center_view_model_matrix = view_matrix * center_matrix; + shader->set_uniform("view_model_matrix", center_view_model_matrix); + const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", center_view_normal_matrix); + m_sphere.set_color(HOVER_COLOR); + m_sphere.render(); - // Now draw the circle itself - let's take a funny shortcut: - Vec3d rad = n.cross(Vec3d::UnitX()); - if (rad.squaredNorm() < 0.1) - rad = n.cross(Vec3d::UnitY()); - rad *= radius * rad.norm(); - const int N = 32; - m_vbo_sphere.set_color(HOVER_COLOR); - for (int i = 0; i < N; ++i) { - rad = Eigen::AngleAxisd(2.0 * M_PI / N, n) * rad; - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(center + rad) * Geometry::scale_transform(N / 100.0 * inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.render(); - } + m_circle.reset(); + m_circle.init_from(smooth_torus(64, 16, float(radius), 5.0f * inv_zoom)); + + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; + shader->set_uniform("view_model_matrix", circle_view_model_matrix); + const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); + m_circle.set_color(HOVER_COLOR); + m_circle.render(); break; } case Measure::SurfaceFeatureType::Edge: @@ -232,23 +226,8 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_cylinder.set_color(HOVER_COLOR); - m_vbo_cylinder.render(); - -/* - std::optional extra_point = feature.get_extra_point(); - if (extra_point.has_value()) { - const Vec3d pin = *extra_point; - const Transform3d feature_matrix = Geometry::translation_transform(pin + selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * - Geometry::scale_transform(inv_zoom) * model_matrix; - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_vbo_sphere.set_color(HOVER_COLOR); - m_vbo_sphere.render(); - } -*/ + m_cylinder.set_color(HOVER_COLOR); + m_cylinder.render(); break; } case Measure::SurfaceFeatureType::Plane: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 8231711710..23abdb3149 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -32,8 +32,9 @@ class GLGizmoMeasure : public GLGizmoBase private: std::unique_ptr m_measuring; - GLModel m_vbo_sphere; - GLModel m_vbo_cylinder; + GLModel m_sphere; + GLModel m_cylinder; + GLModel m_circle; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 62a28c7baf6807fc3b14c069957806e355432553 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 12:00:04 +0200 Subject: [PATCH 209/327] Measuring: refactoring related to plane models cache --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 41 +++++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4e7cd3db1a..68fbd2aaa0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -233,23 +233,28 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Plane: { const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models.size()); + assert(idx < m_plane_models_cache.size()); const Transform3d view_model_matrix = view_matrix * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models[idx]->set_color(HOVER_COLOR); - m_plane_models[idx]->render(); + m_plane_models_cache[idx].set_color(HOVER_COLOR); + m_plane_models_cache[idx].render(); break; } } } #if ENABLE_DEBUG_DIALOG - if (m_show_planes) - for (const auto& glmodel : m_plane_models) { - glmodel->set_color(HOVER_COLOR); - glmodel->render(); + if (m_show_planes) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + for (GLModel& glmodel : m_plane_models_cache) { + glmodel.set_color(HOVER_COLOR); + glmodel.render(); } + } #endif // ENABLE_DEBUG_DIALOG } @@ -279,17 +284,14 @@ void GLGizmoMeasure::on_render_for_picking() void GLGizmoMeasure::update_if_needed() { - auto do_update = [this](const ModelObject* object, const ModelVolume* volume) { - const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; - m_measuring.reset(new Measure::Measuring(its)); - m_plane_models.clear(); + auto update_plane_models_cache = [this](const indexed_triangle_set& its) { + m_plane_models_cache.clear(); const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); for (const std::vector& triangle_indices : planes_triangles) { - m_plane_models.emplace_back(std::unique_ptr(new GLModel())); + m_plane_models_cache.emplace_back(GLModel()); GLModel::Geometry init_data; init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - init_data.color = ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f); - int i = 0; + unsigned int i = 0; for (int idx : triangle_indices) { const Vec3f& v0 = its.vertices[its.indices[idx][0]]; const Vec3f& v1 = its.vertices[its.indices[idx][1]]; @@ -302,8 +304,15 @@ void GLGizmoMeasure::update_if_needed() init_data.add_triangle(i, i + 1, i + 2); i += 3; } - m_plane_models.back()->init_from(std::move(init_data)); + m_plane_models_cache.back().init_from(std::move(init_data)); } + }; + + auto do_update = [this, update_plane_models_cache](const ModelObject* object, const ModelVolume* volume) { + const indexed_triangle_set& its = (volume != nullptr) ? volume->mesh().its : object->volumes.front()->mesh().its; + m_measuring.reset(new Measure::Measuring(its)); + + update_plane_models_cache(its); // Let's save what we calculated it from: m_volumes_matrices.clear(); @@ -315,7 +324,7 @@ void GLGizmoMeasure::update_if_needed() m_volumes_matrices.push_back(vol->get_matrix()); m_volumes_types.push_back(vol->type()); } - m_first_instance_scale = object->instances.front()->get_scaling_factor(); + m_first_instance_scale = object->instances.front()->get_scaling_factor(); m_first_instance_mirror = object->instances.front()->get_mirror(); } m_old_model_object = object; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 23abdb3149..2cbdd15061 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -35,6 +35,7 @@ private: GLModel m_sphere; GLModel m_cylinder; GLModel m_circle; + std::vector m_plane_models_cache; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -54,7 +55,6 @@ private: bool m_show_all = false; bool m_show_planes = false; #endif // ENABLE_DEBUG_DIALOG - std::vector> m_plane_models; void update_if_needed(); From 86b390e2377393a07a562a6f4b8515b55888b264 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 14:15:20 +0200 Subject: [PATCH 210/327] Refactoring into GLGizmoMeasure::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 67 +++++++++++++----------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 68fbd2aaa0..dd562a6490 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -114,34 +114,20 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { +#if !ENABLE_DEBUG_DIALOG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; +#endif // !ENABLE_DEBUG_DIALOG const Selection& selection = m_parent.get_selection(); - GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); - if (shader == nullptr) - return; - - shader->start_using(); - shader->set_uniform("emission_factor", 0.25f); - - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glEnable(GL_BLEND)); - if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && selection.is_single_full_instance()) || (selection.is_single_volume() || selection.is_single_volume_instance())) { + update_if_needed(); + const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - const float inv_zoom = camera.get_inv_zoom(); - - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - - update_if_needed(); Vec3f pos; Vec3f normal; @@ -165,19 +151,42 @@ void GLGizmoMeasure::on_render() if (m_show_all) { features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); } else { + if (!m_parent.is_mouse_dragging()) { #endif // ENABLE_DEBUG_DIALOG - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat.has_value()) + features.emplace_back(*feat); #if ENABLE_DEBUG_DIALOG + } } #endif // ENABLE_DEBUG_DIALOG - + +#if ENABLE_DEBUG_DIALOG + if (features.empty() && !m_show_planes) +#else + if (features.empty()) +#endif // ENABLE_DEBUG_DIALOG + return; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("emission_factor", 0.25f); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + const Transform3d& view_matrix = camera.get_view_matrix(); + const float inv_zoom = camera.get_inv_zoom(); + for (const Measure::SurfaceFeature& feature : features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: @@ -256,18 +265,12 @@ void GLGizmoMeasure::on_render() } } #endif // ENABLE_DEBUG_DIALOG + shader->stop_using(); } - - glsafe(::glEnable(GL_CULL_FACE)); - glsafe(::glDisable(GL_BLEND)); - - shader->stop_using(); } - - #if ! ENABLE_LEGACY_OPENGL_REMOVAL #error NOT IMPLEMENTED #endif From 7b437861247a0fea5569bb68eeb759aaf4699f25 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 24 Aug 2022 15:02:46 +0200 Subject: [PATCH 211/327] Added tech ENABLE_MEASURE_GIZMO_DEBUG to embed debug code related to GLGizmoMeasure --- src/libslic3r/Measure.cpp | 3 ++ src/libslic3r/Measure.hpp | 4 +- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 50 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 6 +-- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 8c7254f85b..9cb46edfc1 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1,3 +1,4 @@ +#include "libslic3r/libslic3r.h" #include "Measure.hpp" #include "libslic3r/Geometry/Circle.hpp" @@ -407,10 +408,12 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} +#if ENABLE_MEASURE_GIZMO_DEBUG std::vector Measuring::get_all_features() const { return priv->get_all_features(); } +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index fe0b1687a1..98517b991b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -69,9 +69,11 @@ public: explicit Measuring(const indexed_triangle_set& its); ~Measuring(); +#if ENABLE_MEASURE_GIZMO_DEBUG // Return a reference to a list of all features identified on the its. // Use only for debugging. Expensive, do not call often. - [[deprecated]] std::vector get_all_features() const; + std::vector get_all_features() const; +#endif // ENABLE_MEASURE_GIZMO_DEBUG // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ad23b4cbe1..d5d2542cc5 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -66,6 +66,8 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) +// Enable debug code for Measure Gizmo +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_2_5_0_ALPHA1) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index dd562a6490..d7a8f53f46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -114,11 +114,11 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { -#if !ENABLE_DEBUG_DIALOG +#if !ENABLE_MEASURE_GIZMO_DEBUG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; -#endif // !ENABLE_DEBUG_DIALOG +#endif // !ENABLE_MEASURE_GIZMO_DEBUG const Selection& selection = m_parent.get_selection(); @@ -134,7 +134,7 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG m_imgui->begin(std::string("DEBUG")); m_imgui->checkbox(wxString("Show all features"), m_show_all); m_imgui->checkbox(wxString("Show all planes"), m_show_planes); @@ -144,33 +144,35 @@ void GLGizmoMeasure::on_render() m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); m_imgui->end(); -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::vector features; -#if ENABLE_DEBUG_DIALOG - if (m_show_all) { - features = m_measuring->get_all_features(); // EXPENSIVE - debugging only. - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); +#if ENABLE_MEASURE_GIZMO_DEBUG + if (m_show_all || m_show_planes) { + features = m_measuring->get_all_features(); + if (!m_show_planes) + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature& f) { + return f.get_type() == Measure::SurfaceFeatureType::Plane; + }), features.end()); + if (!m_show_all) + features.erase(std::remove_if(features.begin(), features.end(), + [](const Measure::SurfaceFeature& f) { + return f.get_type() != Measure::SurfaceFeatureType::Plane; + }), features.end()); } else { if (!m_parent.is_mouse_dragging()) { -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); if (feat.has_value()) features.emplace_back(*feat); -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG } } -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG -#if ENABLE_DEBUG_DIALOG - if (features.empty() && !m_show_planes) -#else if (features.empty()) -#endif // ENABLE_DEBUG_DIALOG return; GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -253,18 +255,6 @@ void GLGizmoMeasure::on_render() } } } -#if ENABLE_DEBUG_DIALOG - if (m_show_planes) { - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - for (GLModel& glmodel : m_plane_models_cache) { - glmodel.set_color(HOVER_COLOR); - glmodel.render(); - } - } -#endif // ENABLE_DEBUG_DIALOG shader->stop_using(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 2cbdd15061..129175f159 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -11,8 +11,6 @@ #include -#define ENABLE_DEBUG_DIALOG 0 - namespace Slic3r { class ModelVolume; @@ -51,10 +49,10 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; -#if ENABLE_DEBUG_DIALOG +#if ENABLE_MEASURE_GIZMO_DEBUG bool m_show_all = false; bool m_show_planes = false; -#endif // ENABLE_DEBUG_DIALOG +#endif // ENABLE_MEASURE_GIZMO_DEBUG void update_if_needed(); From aeb8dec4638be669c5862e34cbcc5adbff4200f7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 25 Aug 2022 13:01:26 +0200 Subject: [PATCH 212/327] Measuring: code for Measure gizmo embedded into new tech ENABLE_MEASURE_GIZMO Fixed conflicts while rebasing to master --- src/libslic3r/Technologies.hpp | 4 +++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 18 ++++-------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 11 ++++------- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 ++++ 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d5d2542cc5..4b6db1da45 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -66,8 +66,10 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) +// Enable Measure Gizmo +#define ENABLE_MEASURE_GIZMO (1 && ENABLE_LEGACY_OPENGL_REMOVAL) // Enable debug code for Measure Gizmo -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_2_5_0_ALPHA1) +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d7a8f53f46..73ee87840a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -14,6 +14,8 @@ #include +#if ENABLE_MEASURE_GIZMO + namespace Slic3r { namespace GUI { @@ -261,20 +263,6 @@ void GLGizmoMeasure::on_render() -#if ! ENABLE_LEGACY_OPENGL_REMOVAL - #error NOT IMPLEMENTED -#endif -#if ! ENABLE_GL_SHADERS_ATTRIBUTES - #error NOT IMPLEMENTED -#endif - -void GLGizmoMeasure::on_render_for_picking() -{ -} - - - - void GLGizmoMeasure::update_if_needed() { auto update_plane_models_cache = [this](const indexed_triangle_set& its) { @@ -354,3 +342,5 @@ void GLGizmoMeasure::update_if_needed() } // namespace GUI } // namespace Slic3r + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 129175f159..218b5e8757 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -1,15 +1,10 @@ #ifndef slic3r_GLGizmoMeasure_hpp_ #define slic3r_GLGizmoMeasure_hpp_ +#if ENABLE_MEASURE_GIZMO + #include "GLGizmoBase.hpp" -#if ENABLE_LEGACY_OPENGL_REMOVAL #include "slic3r/GUI/GLModel.hpp" -#else -#include "slic3r/GUI/3DScene.hpp" -#endif // ENABLE_LEGACY_OPENGL_REMOVAL - - -#include namespace Slic3r { @@ -81,4 +76,6 @@ protected: } // namespace GUI } // namespace Slic3r +#endif // ENABLE_MEASURE_GIZMO + #endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 6d25f84fbc..ff24dd84a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,7 +21,9 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#if ENABLE_MEASURE_GIZMO #include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" +#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -107,7 +109,9 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); +#if ENABLE_MEASURE_GIZMO m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); +#endif // ENABLE_MEASURE_GIZMO m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); From 6c0aff0d23cb450671c41d50901707e2b7326ee5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 29 Aug 2022 12:55:34 +0200 Subject: [PATCH 213/327] Measuring: Measure gizmo features registered for raycasted picking --- src/libslic3r/Measure.hpp | 10 ++ src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 4 +- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/GLModel.cpp | 15 +++ src/slic3r/GUI/GLModel.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 132 ++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 16 ++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 +- 10 files changed, 160 insertions(+), 29 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 98517b991b..2f27a72598 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -49,6 +49,16 @@ public: // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } + bool operator == (const SurfaceFeature& other) const { + if (this->m_type != other.m_type) return false; + if (!this->m_pt1.isApprox(other.m_pt1)) return false; + if (!this->m_pt2.isApprox(other.m_pt2)) return false; + if (this->m_pt3.has_value() && !other.m_pt3.has_value()) return false; + if (!this->m_pt3.has_value() && other.m_pt3.has_value()) return false; + if (this->m_pt3.has_value() && other.m_pt3.has_value() && !(*this->m_pt3).isApprox(*other.m_pt3)) return false; + return this->m_value == other.m_value; + } + private: SurfaceFeatureType m_type = SurfaceFeatureType::Undef; Vec3d m_pt1; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 4b6db1da45..baadc31bfd 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -67,7 +67,7 @@ #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo -#define ENABLE_MEASURE_GIZMO (1 && ENABLE_LEGACY_OPENGL_REMOVAL) +#define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) // Enable debug code for Measure Gizmo #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 04a6918711..c63b70f9ea 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2252,7 +2252,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_LEGACY_OPENGL_REMOVAL volume.model.init_from(mesh); #if ENABLE_RAYCAST_PICKING - volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); + volume.mesh_raycaster = std::make_unique(std::make_shared(mesh)); #endif // ENABLE_RAYCAST_PICKING #else volume.indexed_vertex_array.load_mesh(mesh); @@ -2272,7 +2272,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re #if ENABLE_RAYCAST_PICKING const TriangleMesh& new_mesh = m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(); volume.model.init_from(new_mesh); - volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); + volume.mesh_raycaster = std::make_unique(std::make_shared(new_mesh)); #else volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); #endif // ENABLE_RAYCAST_PICKING diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0adf1cb26d..34c6ed4ee0 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -680,7 +680,7 @@ public: #if ENABLE_RAYCAST_PICKING std::shared_ptr add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster, - const Transform3d& trafo, bool use_back_faces = false) { + const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) { return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces); } void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) { diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 03d2677bea..70af07b7d7 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -257,6 +257,21 @@ void GLModel::Geometry::remove_vertex(size_t id) } } +indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const +{ + indexed_triangle_set its; + its.vertices.reserve(vertices_count()); + for (size_t i = 0; i < vertices_count(); ++i) { + its.vertices.emplace_back(extract_position_3(i)); + } + its.indices.reserve(indices_count() / 3); + for (size_t i = 0; i < indices_count() / 3; ++i) { + const size_t tri_id = i * 3; + its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2)); + } + return its; +} + size_t GLModel::Geometry::vertex_stride_floats(const Format& format) { switch (format.vertex_layout) diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index e631952a3e..2d1e352c48 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -140,6 +140,8 @@ namespace GUI { size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); } + indexed_triangle_set get_as_indexed_triangle_set() const; + static size_t vertex_stride_floats(const Format& format); static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index bbae3f2429..99769e0061 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -114,7 +114,7 @@ void GLGizmoHollow::on_register_raycasters_for_picking() if (info != nullptr && !info->model_object()->sla_drain_holes.empty()) { const sla::DrainHoles& drain_holes = info->model_object()->sla_drain_holes; for (int i = 0; i < (int)drain_holes.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cylinder.mesh_raycaster)); } update_raycasters_for_picking_transform(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 73ee87840a..e132cf2ce1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -7,7 +7,6 @@ #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/Measure.hpp" #include "libslic3r/PresetBundle.hpp" #include @@ -20,12 +19,22 @@ namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const int POINT_ID = 100; +static const int EDGE_ID = 200; +static const int CIRCLE_ID = 300; +static const int CIRCLE_CENTER_ID = 301; +static const int PLANE_ID = 400; GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { - m_sphere.init_from(smooth_sphere(16, 7.5f)); - m_cylinder.init_from(smooth_cylinder(16, 5.0f, 1.0f)); + GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(sphere_geometry.get_as_indexed_triangle_set()))); + m_sphere.model.init_from(std::move(sphere_geometry)); + + GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(cylinder_geometry.get_as_indexed_triangle_set()))); + m_cylinder.model.init_from(std::move(cylinder_geometry)); } bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) @@ -130,6 +139,7 @@ void GLGizmoMeasure::on_render() const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); + const float inv_zoom = (float)camera.get_inv_zoom(); Vec3f pos; Vec3f normal; @@ -174,8 +184,70 @@ void GLGizmoMeasure::on_render() } #endif // ENABLE_MEASURE_GIZMO_DEBUG - if (features.empty()) - return; + if (m_features != features) { + GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_features = features; + if (m_features.empty()) + return; + +#if !ENABLE_MEASURE_GIZMO_DEBUG + for (const Measure::SurfaceFeature& feature : m_features) { + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); + + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } + } +#endif // !ENABLE_MEASURE_GIZMO_DEBUG + } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -189,9 +261,8 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); const Transform3d& view_matrix = camera.get_view_matrix(); - const float inv_zoom = camera.get_inv_zoom(); - for (const Measure::SurfaceFeature& feature : features) { + for (const Measure::SurfaceFeature& feature : m_features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: { @@ -201,8 +272,11 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.set_color(HOVER_COLOR); - m_sphere.render(); + m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); break; } case Measure::SurfaceFeatureType::Circle: @@ -214,19 +288,22 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", center_view_model_matrix); const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.set_color(HOVER_COLOR); - m_sphere.render(); - - m_circle.reset(); - m_circle.init_from(smooth_torus(64, 16, float(radius), 5.0f * inv_zoom)); + m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.render(); + auto it = m_raycasters.find(CIRCLE_CENTER_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; shader->set_uniform("view_model_matrix", circle_view_model_matrix); const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.set_color(HOVER_COLOR); - m_circle.render(); + m_circle.model.set_color(HOVER_COLOR); + m_circle.model.render(); + it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); break; } case Measure::SurfaceFeatureType::Edge: @@ -239,8 +316,11 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.set_color(HOVER_COLOR); - m_cylinder.render(); + m_cylinder.model.set_color(HOVER_COLOR); + m_cylinder.model.render(); + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); break; } case Measure::SurfaceFeatureType::Plane: @@ -253,6 +333,9 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); m_plane_models_cache[idx].set_color(HOVER_COLOR); m_plane_models_cache[idx].render(); + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); break; } } @@ -340,6 +423,19 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::on_register_raycasters_for_picking() +{ + // the features 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 GLGizmoMeasure::on_unregister_raycasters_for_picking() +{ + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); + m_parent.set_raycaster_gizmos_on_top(false); + m_raycasters.clear(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 218b5e8757..41dc9fffba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "libslic3r/Measure.hpp" namespace Slic3r { @@ -25,10 +26,14 @@ class GLGizmoMeasure : public GLGizmoBase private: std::unique_ptr m_measuring; - GLModel m_sphere; - GLModel m_cylinder; - GLModel m_circle; + PickingModel m_sphere; + PickingModel m_cylinder; + PickingModel m_circle; + PickingModel m_plane; + std::vector m_plane_models_cache; + std::map> m_raycasters; + std::vector m_features; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -53,7 +58,7 @@ private: public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - + /// /// Apply rotation on select plane /// @@ -71,6 +76,9 @@ protected: void on_render_for_picking() override; void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; + + virtual void on_register_raycasters_for_picking() override; + virtual void on_unregister_raycasters_for_picking() override; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b562eb49a0..7be4301ad1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -148,8 +148,8 @@ void GLGizmoSlaSupports::on_register_raycasters_for_picking() if (m_editing_mode && !m_editing_cache.empty()) { for (size_t i = 0; i < m_editing_cache.size(); ++i) { - m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster, Transform3d::Identity()), - m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster, Transform3d::Identity())); + m_raycasters.emplace_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_sphere.mesh_raycaster), + m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, i, *m_cone.mesh_raycaster)); } update_raycasters_for_picking_transform(); } From ed287215dbf6161e5d259265959b0fc234ae14cf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 14:30:38 +0200 Subject: [PATCH 214/327] Measuring: Added Measure gizmo imgui dialog + removed tech ENABLE_MEASURE_GIZMO_DEBUG + locking of features by pressing CTRL key --- src/libslic3r/Measure.cpp | 2 - src/libslic3r/Measure.hpp | 6 +- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.hpp | 5 + src/slic3r/GUI/GUI_Utils.hpp | 12 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 365 ++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 19 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 58 ++-- 9 files changed, 311 insertions(+), 162 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 9cb46edfc1..cdde276dda 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -408,12 +408,10 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -#if ENABLE_MEASURE_GIZMO_DEBUG std::vector Measuring::get_all_features() const { return priv->get_all_features(); } -#endif // ENABLE_MEASURE_GIZMO_DEBUG std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 2f27a72598..95d8f34cba 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -59,6 +59,10 @@ public: return this->m_value == other.m_value; } + bool operator != (const SurfaceFeature& other) const { + return !operator == (other); + } + private: SurfaceFeatureType m_type = SurfaceFeatureType::Undef; Vec3d m_pt1; @@ -79,11 +83,9 @@ public: explicit Measuring(const indexed_triangle_set& its); ~Measuring(); -#if ENABLE_MEASURE_GIZMO_DEBUG // Return a reference to a list of all features identified on the its. // Use only for debugging. Expensive, do not call often. std::vector get_all_features() const; -#endif // ENABLE_MEASURE_GIZMO_DEBUG // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index baadc31bfd..a96a39066c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -68,8 +68,6 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) -// Enable debug code for Measure Gizmo -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 34c6ed4ee0..7ce488ae54 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -19,6 +19,9 @@ #if ENABLE_RAYCAST_PICKING #include "SceneRaycaster.hpp" #endif // ENABLE_RAYCAST_PICKING +#if ENABLE_MEASURE_GIZMO +#include "GUI_Utils.hpp" +#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/Slicing.hpp" @@ -135,6 +138,7 @@ private: wxTimer* m_timer; }; +#if !ENABLE_MEASURE_GIZMO class KeyAutoRepeatFilter { size_t m_count{ 0 }; @@ -144,6 +148,7 @@ public: void reset_count() { m_count = 0; } bool is_first() const { return m_count == 0; } }; +#endif // !ENABLE_MEASURE_GIZMO wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index d7d3529fee..e2152edd6d 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -416,6 +416,18 @@ public: ~TaskTimer(); }; +#if ENABLE_MEASURE_GIZMO +class KeyAutoRepeatFilter +{ + size_t m_count{ 0 }; + +public: + void increase_count() { ++m_count; } + void reset_count() { m_count = 0; } + bool is_first() const { return m_count == 0; } +}; +#endif // ENABLE_MEASURE_GIZMO + }} #endif diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e132cf2ce1..2a856fb830 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -15,10 +15,26 @@ #if ENABLE_MEASURE_GIZMO +std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } + case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Point"); } + case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } + case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } + case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + } +} + namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.2f, 0.8f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; + static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; @@ -42,40 +58,39 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_pos_x = mouse_event.GetX(); m_mouse_pos_y = mouse_event.GetY(); - if (mouse_event.Moving()) { // only for sure m_mouse_left_down = false; return false; } - if (mouse_event.LeftDown()) { + else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; - + m_mouse_left_down = true; return true; } // fix: prevent restart gizmo when reselect object // take responsibility for left up - if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; + if (m_parent.get_first_hover_volume_idx() >= 0) + m_mouse_left_down = true; - } else if (mouse_event.LeftUp()) { + } + else if (mouse_event.LeftUp()) { if (m_mouse_left_down) { // responsible for mouse left up after selecting plane m_mouse_left_down = false; return true; } - } else if (mouse_event.Leaving()) { - m_mouse_left_down = false; } + else if (mouse_event.Leaving()) + m_mouse_left_down = false; + return false; } - - void GLGizmoMeasure::data_changed() { - const Selection & selection = m_parent.get_selection(); + const Selection& selection = m_parent.get_selection(); const ModelObject* model_object = nullptr; const ModelVolume* model_volume = nullptr; if (selection.is_single_full_instance() || @@ -87,29 +102,43 @@ void GLGizmoMeasure::data_changed() update_if_needed(); } - - -bool GLGizmoMeasure::on_init() +bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { - // FIXME m_shortcut_key = WXK_CONTROL_F; + if (action == SLAGizmoEventType::CtrlDown) { + if (m_ctrl_kar_filter.is_first()) { + if (!m_features.empty()) + m_mode = EMode::ExtendedSelection; + } + + m_ctrl_kar_filter.increase_count(); + } + else if (action == SLAGizmoEventType::CtrlUp) { + m_ctrl_kar_filter.reset_count(); + m_mode = EMode::BasicSelection; + } + return true; } - +bool GLGizmoMeasure::on_init() +{ + m_shortcut_key = WXK_CONTROL_U; + return true; +} void GLGizmoMeasure::on_set_state() { + if (m_state == Off) + m_ctrl_kar_filter.reset_count(); + else + m_mode = EMode::BasicSelection; } - - CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const { return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::Raycaster)); } - - std::string GLGizmoMeasure::on_get_name() const { return _u8L("Measure"); @@ -125,11 +154,9 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { -#if !ENABLE_MEASURE_GIZMO_DEBUG // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; -#endif // !ENABLE_MEASURE_GIZMO_DEBUG const Selection& selection = m_parent.get_selection(); @@ -146,107 +173,76 @@ void GLGizmoMeasure::on_render() size_t facet_idx; m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); -#if ENABLE_MEASURE_GIZMO_DEBUG - m_imgui->begin(std::string("DEBUG")); - m_imgui->checkbox(wxString("Show all features"), m_show_all); - m_imgui->checkbox(wxString("Show all planes"), m_show_planes); - ImGui::Separator(); - m_imgui->text(std::string("face_idx: ") + std::to_string(facet_idx)); - m_imgui->text(std::string("pos_x: ") + std::to_string(pos.x())); - m_imgui->text(std::string("pos_y: ") + std::to_string(pos.y())); - m_imgui->text(std::string("pos_z: ") + std::to_string(pos.z())); - m_imgui->end(); -#endif // ENABLE_MEASURE_GIZMO_DEBUG - std::vector features; -#if ENABLE_MEASURE_GIZMO_DEBUG - if (m_show_all || m_show_planes) { - features = m_measuring->get_all_features(); - if (!m_show_planes) - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() == Measure::SurfaceFeatureType::Plane; - }), features.end()); - if (!m_show_all) - features.erase(std::remove_if(features.begin(), features.end(), - [](const Measure::SurfaceFeature& f) { - return f.get_type() != Measure::SurfaceFeatureType::Plane; - }), features.end()); + if (m_mode == EMode::BasicSelection) { + std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); + if (feat.has_value()) + features.emplace_back(*feat); } - else { - if (!m_parent.is_mouse_dragging()) { -#endif // ENABLE_MEASURE_GIZMO_DEBUG - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); -#if ENABLE_MEASURE_GIZMO_DEBUG - } - } -#endif // ENABLE_MEASURE_GIZMO_DEBUG - if (m_features != features) { - GLGizmoMeasure::on_unregister_raycasters_for_picking(); - m_features = features; - if (m_features.empty()) - return; + if (m_mode == EMode::BasicSelection) { + if (m_features != features) { + GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_features = features; + if (m_features.empty()) + return; -#if !ENABLE_MEASURE_GIZMO_DEBUG - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); - - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; - - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; + for (const Measure::SurfaceFeature& feature : m_features) { + switch (feature.get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } + } } } -#endif // !ENABLE_MEASURE_GIZMO_DEBUG } GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); @@ -262,6 +258,13 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); + ColorRGBA color; + switch (m_mode) + { + case EMode::BasicSelection: { color = BASIC_HOVER_COLOR; break; } + case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } + } + for (const Measure::SurfaceFeature& feature : m_features) { switch (feature.get_type()) { case Measure::SurfaceFeatureType::Point: @@ -272,7 +275,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.set_color(color); m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -288,7 +291,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", center_view_model_matrix); const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(HOVER_COLOR); + m_sphere.model.set_color(color); m_sphere.model.render(); auto it = m_raycasters.find(CIRCLE_CENTER_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -299,7 +302,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", circle_view_model_matrix); const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(HOVER_COLOR); + m_circle.model.set_color(color); m_circle.model.render(); it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -316,7 +319,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(HOVER_COLOR); + m_cylinder.model.set_color(color); m_cylinder.model.render(); auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -331,7 +334,7 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_model_matrix", view_model_matrix); const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(HOVER_COLOR); + m_plane_models_cache[idx].set_color(color); m_plane_models_cache[idx].render(); auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -344,8 +347,6 @@ void GLGizmoMeasure::on_render() } } - - void GLGizmoMeasure::update_if_needed() { auto update_plane_models_cache = [this](const indexed_triangle_set& its) { @@ -423,6 +424,124 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) +{ + static Measure::SurfaceFeature last_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + + static float last_y = 0.0f; + static float last_h = 0.0f; + + m_imgui->begin(_L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + // 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 (m_features.empty()) + m_imgui->text(_u8L("Select features to measure")); + + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; + + const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + Measure::SurfaceFeature curr_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + for (size_t i = 0; i < m_features.size(); ++i) { + curr_feature = m_features[i]; + const Measure::SurfaceFeatureType type = curr_feature.get_type(); + if (type != Measure::SurfaceFeatureType::Undef) { + const std::string header = surface_feature_type_as_string(type) + std::string("##") + std::to_string(i); + if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("Data", 2)) { + switch (type) + { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * curr_feature.get_point(); + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); + add_row_to_table(_u8L("Position") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = curr_feature.get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", from.x(), from.y(), from.z()); + add_row_to_table(_u8L("From") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", to.x(), to.y(), to.z()); + add_row_to_table(_u8L("To") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = curr_feature.get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", center.x(), center.y(), center.z()); + add_row_to_table(_u8L("Center") + ":", std::string(buf)); + sprintf(buf, "%.3f", radius); + add_row_to_table(_u8L("Radius") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); + add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = curr_feature.get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", origin.x(), origin.y(), origin.z()); + add_row_to_table(_u8L("Origin") + ":", std::string(buf)); + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); + add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + break; + } + } + ImGui::EndTable(); + } + } + } + } + + if (last_feature != curr_feature) { + // the dialog may have changed its size, ask for an extra frame to render it properly + last_feature = curr_feature; + m_imgui->set_requires_extra_frame(); + } + + if (!m_features.empty()) { + static const std::string ctrl = +#ifdef __APPLE__ + "⌘" +#else + "Ctrl" +#endif //__APPLE__ + ; + + ImGui::Separator(); + m_imgui->text(_u8L("Press") + " " + ctrl + " " + _u8L("to enable extended selection")); + } + m_imgui->end(); +} + void GLGizmoMeasure::on_register_raycasters_for_picking() { // the features are rendered on top of the scene, so the raytraced picker should take it into account diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 41dc9fffba..a3d027dccd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -5,6 +5,7 @@ #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" +#include "slic3r/GUI/GUI_Utils.hpp" #include "libslic3r/Measure.hpp" namespace Slic3r { @@ -18,12 +19,19 @@ namespace Measure { class Measuring; } namespace GUI { +enum class SLAGizmoEventType : unsigned char; class GLGizmoMeasure : public GLGizmoBase { // This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. -private: + enum class EMode : unsigned char + { + BasicSelection, + ExtendedSelection + }; + + EMode m_mode{ EMode::BasicSelection }; std::unique_ptr m_measuring; PickingModel m_sphere; @@ -49,10 +57,8 @@ private: int m_mouse_pos_x; int m_mouse_pos_y; -#if ENABLE_MEASURE_GIZMO_DEBUG - bool m_show_all = false; - bool m_show_planes = false; -#endif // ENABLE_MEASURE_GIZMO_DEBUG + + KeyAutoRepeatFilter m_ctrl_kar_filter; void update_if_needed(); @@ -68,6 +74,8 @@ public: void data_changed() override; + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + protected: bool on_init() override; std::string on_get_name() const override; @@ -77,6 +85,7 @@ protected: void on_set_state() override; CommonGizmosDataID on_get_requirements() const override; + virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual void on_register_raycasters_for_picking() override; virtual void on_unregister_raycasters_for_picking() override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index c8b29f761e..c3aa1691f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -24,6 +24,10 @@ enum class SLAGizmoEventType : unsigned char { Dragging, Delete, SelectAll, +#if ENABLE_MEASURE_GIZMO + CtrlDown, + CtrlUp, +#endif // ENABLE_MEASURE_GIZMO ShiftUp, AltUp, ApplyChanges, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ff24dd84a3..fbf0afe13a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -294,6 +294,10 @@ 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); +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure) + return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); +#endif // ENABLE_MEASURE_GIZMO else return false; } @@ -499,8 +503,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) bool processed = false; - if ((evt.GetModifiers() & ctrlMask) != 0) - { + if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { #ifdef __APPLE__ @@ -518,15 +521,13 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } } - else if (!evt.HasModifiers()) - { + else if (!evt.HasModifiers()) { switch (keyCode) { // key ESC case WXK_ESCAPE: { - if (m_current != Undefined) - { + if (m_current != Undefined) { if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges)) reset_all_states(); @@ -563,8 +564,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'A': case 'a': { - if (m_current == SlaSupports) - { + if (m_current == SlaSupports) { gizmo_event(SLAGizmoEventType::AutomaticGeneration); // set as processed no matter what's returned by gizmo_event() to avoid the calling canvas to process 'A' as arrange processed = true; @@ -582,8 +582,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'F': case 'f': { - if (m_current == Scale) - { + if (m_current == Scale) { if (!is_dragging()) wxGetApp().plater()->scale_selection_to_fit_print_volume(); @@ -595,8 +594,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } } - if (!processed && !evt.HasModifiers()) - { + if (!processed && !evt.HasModifiers()) { if (handle_shortcut(keyCode)) processed = true; } @@ -612,10 +610,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) const int keyCode = evt.GetKeyCode(); bool processed = false; - if (evt.GetEventType() == wxEVT_KEY_UP) - { - if (m_current == SlaSupports || m_current == Hollow) - { + if (evt.GetEventType() == wxEVT_KEY_UP) { + if (m_current == SlaSupports || m_current == Hollow) { bool is_editing = true; bool is_rectangle_dragging = false; @@ -629,33 +625,33 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); } - if (keyCode == WXK_SHIFT) - { + if (keyCode == WXK_SHIFT) { // shift has been just released - SLA gizmo might want to close rectangular selection. if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging)) processed = true; } - else if (keyCode == WXK_ALT) - { + else if (keyCode == WXK_ALT) { // alt has been just released - SLA gizmo might want to close rectangular selection. if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging)) processed = true; } } +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure && keyCode == WXK_CONTROL) { + gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false); + } +#endif // ENABLE_MEASURE_GIZMO // if (processed) // m_parent.set_cursor(GLCanvas3D::Standard); } - 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()) - { + 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()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } - else if (m_current == Cut) - { + 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()); @@ -668,11 +664,17 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } default: { break; } } - } else if (m_current == Simplify && keyCode == WXK_ESCAPE) { + } + else if (m_current == Simplify && keyCode == WXK_ESCAPE) { GLGizmoSimplify *simplify = dynamic_cast(get_current()); if (simplify != nullptr) processed = simplify->on_esc_key_down(); } +#if ENABLE_MEASURE_GIZMO + else if (m_current == Measure && keyCode == WXK_CONTROL) { + gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true); + } +#endif // ENABLE_MEASURE_GIZMO } if (processed) From 9f4f09fbbfb62aa63a5c5916815946a4ce0fed42 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 15:38:29 +0200 Subject: [PATCH 215/327] Refactoring of GLGizmoMeasure to simplify code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 329 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 7 +- 2 files changed, 167 insertions(+), 169 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2a856fb830..2c040c9c3b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -55,8 +55,7 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) { - m_mouse_pos_x = mouse_event.GetX(); - m_mouse_pos_y = mouse_event.GetY(); + m_mouse_pos = { double(mouse_event.GetX()), double(mouse_event.GetY()) }; if (mouse_event.Moving()) { // only for sure @@ -106,7 +105,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po { if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (!m_features.empty()) + if (m_curr_feature.has_value()) m_mode = EMode::ExtendedSelection; } @@ -171,80 +170,84 @@ void GLGizmoMeasure::on_render() Vec3f pos; Vec3f normal; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(Vec2d(m_mouse_pos_x, m_mouse_pos_y), model_matrix, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, pos, normal, nullptr, &facet_idx); - std::vector features; - if (m_mode == EMode::BasicSelection) { - std::optional feat = m_measuring->get_feature(facet_idx, pos.cast()); - if (feat.has_value()) - features.emplace_back(*feat); - } + std::optional curr_feature; + if (m_mode == EMode::BasicSelection) + curr_feature = m_measuring->get_feature(facet_idx, pos.cast()); if (m_mode == EMode::BasicSelection) { - if (m_features != features) { + if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); - m_features = features; - if (m_features.empty()) + m_curr_feature = curr_feature; + if (!m_curr_feature.has_value()) return; - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); + switch (m_curr_feature->get_type()) { + case Measure::SurfaceFeatureType::Point: + { + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = m_curr_feature->get_circle(); - m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; + // TODO: check for changed inv_zoom - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = m_curr_feature->get_plane(); - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); - m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); - break; - } + // TODO: check for changed idx + + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; + + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; } + + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); + m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); + break; + } } } } + if (!m_curr_feature.has_value()) + return; + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; @@ -265,84 +268,84 @@ void GLGizmoMeasure::on_render() case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } } - for (const Measure::SurfaceFeature& feature : m_features) { - switch (feature.get_type()) { - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = feature.get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d center_view_model_matrix = view_matrix * center_matrix; - shader->set_uniform("view_model_matrix", center_view_model_matrix); - const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(color); - m_sphere.model.render(); - auto it = m_raycasters.find(CIRCLE_CENTER_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); - - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; - shader->set_uniform("view_model_matrix", circle_view_model_matrix); - const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(color); - m_circle.model.render(); - it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = feature.get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(color); - m_cylinder.model.render(); - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(color); - m_plane_models_cache[idx].render(); - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); - break; - } - } + switch (m_curr_feature->get_type()) { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = m_curr_feature->get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_sphere.model.set_color(color); + m_sphere.model.render(); + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + break; } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = m_curr_feature->get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + const Transform3d center_view_model_matrix = view_matrix * center_matrix; + shader->set_uniform("view_model_matrix", center_view_model_matrix); + const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", center_view_normal_matrix); + m_sphere.model.set_color(color); + m_sphere.model.render(); + auto it = m_raycasters.find(CIRCLE_CENTER_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; + shader->set_uniform("view_model_matrix", circle_view_model_matrix); + const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); + m_circle.model.set_color(color); + m_circle.model.render(); + it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = m_curr_feature->get_edge(); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Transform3d view_model_matrix = view_matrix * feature_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_cylinder.model.set_color(color); + m_cylinder.model.render(); + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = m_curr_feature->get_plane(); + assert(idx < m_plane_models_cache.size()); + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + m_plane_models_cache[idx].set_color(color); + m_plane_models_cache[idx].render(); + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + break; + } + } + shader->stop_using(); } } @@ -426,7 +429,8 @@ void GLGizmoMeasure::update_if_needed() void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { - static Measure::SurfaceFeature last_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); + static std::optional last_feature; + static EMode last_mode = EMode::BasicSelection; static float last_y = 0.0f; static float last_h = 0.0f; @@ -446,31 +450,27 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - if (m_features.empty()) + if (!m_curr_feature.has_value()) m_imgui->text(_u8L("Select features to measure")); + else { + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; - auto add_row_to_table = [this](const wxString& label, const std::string& value) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); - ImGui::TableSetColumnIndex(1); - m_imgui->text(value); - }; - - const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - Measure::SurfaceFeature curr_feature = Measure::SurfaceFeature(Measure::SurfaceFeatureType::Undef, Vec3d::Zero(), Vec3d::Zero(), std::nullopt, 0.0); - for (size_t i = 0; i < m_features.size(); ++i) { - curr_feature = m_features[i]; - const Measure::SurfaceFeatureType type = curr_feature.get_type(); + const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); if (type != Measure::SurfaceFeatureType::Undef) { - const std::string header = surface_feature_type_as_string(type) + std::string("##") + std::to_string(i); - if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::CollapsingHeader(surface_feature_type_as_string(type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { if (ImGui::BeginTable("Data", 2)) { switch (type) { case Measure::SurfaceFeatureType::Point: { - const Vec3d position = volume_matrix * curr_feature.get_point(); + const Vec3d position = volume_matrix * m_curr_feature->get_point(); char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); add_row_to_table(_u8L("Position") + ":", std::string(buf)); @@ -478,7 +478,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Edge: { - auto [from, to] = curr_feature.get_edge(); + auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; char buf[1024]; @@ -490,7 +490,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Circle: { - auto [center, radius, normal] = curr_feature.get_circle(); + auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; char buf[1024]; @@ -504,7 +504,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } case Measure::SurfaceFeatureType::Plane: { - auto [idx, normal, origin] = curr_feature.get_plane(); + auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; char buf[1024]; @@ -521,14 +521,15 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } - if (last_feature != curr_feature) { + if (last_feature != m_curr_feature || last_mode != m_mode) { // the dialog may have changed its size, ask for an extra frame to render it properly - last_feature = curr_feature; + last_feature = m_curr_feature; + last_mode = m_mode; m_imgui->set_requires_extra_frame(); } - if (!m_features.empty()) { - static const std::string ctrl = + if (m_curr_feature.has_value() && m_mode == EMode::BasicSelection) { + static const std::string ctrl = #ifdef __APPLE__ "⌘" #else diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index a3d027dccd..34a536af96 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -41,7 +41,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; - std::vector m_features; + std::optional m_curr_feature; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -50,13 +50,10 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_mirror{ Vec3d::Ones() }; bool m_mouse_left_down = false; // for detection left_up of this gizmo - bool m_planes_valid = false; const ModelObject* m_old_model_object = nullptr; const ModelVolume* m_old_model_volume = nullptr; - std::vector instances_matrices; - int m_mouse_pos_x; - int m_mouse_pos_y; + Vec2d m_mouse_pos{ Vec2d::Zero() }; KeyAutoRepeatFilter m_ctrl_kar_filter; From 2bb16b1dc88d44090d8c17c2447b94034e954f33 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 30 Aug 2022 15:50:11 +0200 Subject: [PATCH 216/327] Refactoring of GLGizmoMeasure::on_render_input_window to simplify code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 38 +++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 6 ++-- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2c040c9c3b..3e803fa603 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -460,6 +460,16 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text(value); }; + auto format_double = [](double value) { + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); + }; + auto format_vec3 = [](const Vec3d& v) { + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); + }; const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); @@ -471,9 +481,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit case Measure::SurfaceFeatureType::Point: { const Vec3d position = volume_matrix * m_curr_feature->get_point(); - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", position.x(), position.y(), position.z()); - add_row_to_table(_u8L("Position") + ":", std::string(buf)); + add_row_to_table(_u8L("Position") + ":", format_vec3(position)); break; } case Measure::SurfaceFeatureType::Edge: @@ -481,11 +489,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", from.x(), from.y(), from.z()); - add_row_to_table(_u8L("From") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", to.x(), to.y(), to.z()); - add_row_to_table(_u8L("To") + ":", std::string(buf)); + add_row_to_table(_u8L("From") + ":", format_vec3(from)); + add_row_to_table(_u8L("To") + ":", format_vec3(to)); break; } case Measure::SurfaceFeatureType::Circle: @@ -493,13 +498,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", center.x(), center.y(), center.z()); - add_row_to_table(_u8L("Center") + ":", std::string(buf)); - sprintf(buf, "%.3f", radius); - add_row_to_table(_u8L("Radius") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); - add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + add_row_to_table(_u8L("Center") + ":", format_vec3(center)); + add_row_to_table(_u8L("Radius") + ":", format_double(radius)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); break; } case Measure::SurfaceFeatureType::Plane: @@ -507,11 +508,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", origin.x(), origin.y(), origin.z()); - add_row_to_table(_u8L("Origin") + ":", std::string(buf)); - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", normal.x(), normal.y(), normal.z()); - add_row_to_table(_u8L("Normal") + ":", std::string(buf)); + add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); break; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 34a536af96..12e0fbfe1d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -49,9 +49,9 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; - bool m_mouse_left_down = false; // for detection left_up of this gizmo - const ModelObject* m_old_model_object = nullptr; - const ModelVolume* m_old_model_volume = nullptr; + bool m_mouse_left_down{ false }; // for detection left_up of this gizmo + const ModelObject* m_old_model_object{ nullptr }; + const ModelVolume* m_old_model_volume{ nullptr }; Vec2d m_mouse_pos{ Vec2d::Zero() }; From 60aaebc1fe5ece729f7aebb77a5c856cbab0e9a0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 12:42:55 +0200 Subject: [PATCH 217/327] Measuring: Measure gizmo - added visualization of point for extended selection and updates to imgui dialog --- src/slic3r/GUI/GLCanvas3D.cpp | 9 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 255 +++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 3 files changed, 151 insertions(+), 114 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c63b70f9ea..7d8ca616b4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5465,8 +5465,13 @@ void GLCanvas3D::_picking_pass() // do not add the volume id if any gizmo is active and CTRL is pressed if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) { m_hover_volume_idxs.emplace_back(hit.raycaster_id); +#if !ENABLE_MEASURE_GIZMO m_gizmos.set_hover_id(-1); +#endif // !ENABLE_MEASURE_GIZMO } +#if ENABLE_MEASURE_GIZMO + m_gizmos.set_hover_id(-1); +#endif // ENABLE_MEASURE_GIZMO } } else @@ -5477,8 +5482,8 @@ void GLCanvas3D::_picking_pass() case SceneRaycaster::EType::Gizmo: { const Size& cnv_size = get_canvas_size(); - bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && - 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); + const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() && + 0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height(); m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1); break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e803fa603..932f9113f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -21,7 +21,7 @@ std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType t { default: case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Point"); } + case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Vertex"); } case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } @@ -32,15 +32,22 @@ namespace Slic3r { namespace GUI { static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.2f, 0.8f, 0.2f, 1.0f }; +static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.8f, 0.8f, 0.2f, 1.0f }; static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; -static const int CIRCLE_CENTER_ID = 301; static const int PLANE_ID = 400; +static const std::string CTRL_STR = +#ifdef __APPLE__ +"⌘" +#else +"Ctrl" +#endif //__APPLE__ +; + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -127,8 +134,11 @@ bool GLGizmoMeasure::on_init() void GLGizmoMeasure::on_set_state() { - if (m_state == Off) + if (m_state == Off) { m_ctrl_kar_filter.reset_count(); + m_curr_feature.reset(); + m_curr_ex_feature_position.reset(); + } else m_mode = EMode::BasicSelection; } @@ -167,16 +177,19 @@ void GLGizmoMeasure::on_render() const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); - Vec3f pos; - Vec3f normal; + Vec3f position_on_model; + Vec3f normal_on_model; size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, pos, normal, nullptr, &facet_idx); + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &facet_idx); + + const bool is_hovering_on_extended_selection = m_mode == EMode::ExtendedSelection && m_hover_id != -1; std::optional curr_feature; if (m_mode == EMode::BasicSelection) - curr_feature = m_measuring->get_feature(facet_idx, pos.cast()); + curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); if (m_mode == EMode::BasicSelection) { + m_curr_ex_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); m_curr_feature = curr_feature; @@ -196,7 +209,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, n] = m_curr_feature->get_circle(); + const auto& [center, radius, normal] = m_curr_feature->get_circle(); // TODO: check for changed inv_zoom @@ -206,12 +219,12 @@ void GLGizmoMeasure::on_render() m_circle.model.init_from(std::move(circle_geometry)); m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); - m_raycasters.insert({ CIRCLE_CENTER_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_CENTER_ID, *m_sphere.mesh_raycaster) }); + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, pt] = m_curr_feature->get_plane(); + const auto& [idx, normal, point] = m_curr_feature->get_plane(); // TODO: check for changed idx @@ -244,6 +257,8 @@ void GLGizmoMeasure::on_render() } } } + else if (m_mode == EMode::ExtendedSelection) + m_curr_ex_feature_position = position_on_model.cast(); if (!m_curr_feature.has_value()) return; @@ -261,23 +276,27 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); - ColorRGBA color; + ColorRGBA feature_color; switch (m_mode) { - case EMode::BasicSelection: { color = BASIC_HOVER_COLOR; break; } - case EMode::ExtendedSelection: { color = LOCK_COLOR; break; } + case EMode::BasicSelection: { feature_color = BASIC_HOVER_COLOR; break; } + case EMode::ExtendedSelection: { feature_color = LOCK_COLOR; break; } } + auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { + const Transform3d view_model_matrix = view_matrix * model_matrix; + shader->set_uniform("view_model_matrix", view_model_matrix); + const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); + shader->set_uniform("view_normal_matrix", view_normal_matrix); + }; + switch (m_curr_feature->get_type()) { case Measure::SurfaceFeatureType::Point: { const Vec3d& position = m_curr_feature->get_point(); const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_sphere.model.set_color(color); + set_matrix_uniforms(feature_matrix); + m_sphere.model.set_color(is_hovering_on_extended_selection ? EXTENDED_HOVER_COLOR : feature_color); m_sphere.model.render(); auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -289,23 +308,17 @@ void GLGizmoMeasure::on_render() const auto& [center, radius, n] = m_curr_feature->get_circle(); // render center const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - const Transform3d center_view_model_matrix = view_matrix * center_matrix; - shader->set_uniform("view_model_matrix", center_view_model_matrix); - const Matrix3d center_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * center_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", center_view_normal_matrix); - m_sphere.model.set_color(color); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color((is_hovering_on_extended_selection && m_hover_id == POINT_ID) ? EXTENDED_HOVER_COLOR : feature_color); m_sphere.model.render(); - auto it = m_raycasters.find(CIRCLE_CENTER_ID); + auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); // render circle const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - const Transform3d circle_view_model_matrix = view_matrix * circle_matrix; - shader->set_uniform("view_model_matrix", circle_view_model_matrix); - const Matrix3d circle_view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * circle_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", circle_view_normal_matrix); - m_circle.model.set_color(color); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(feature_color); m_circle.model.render(); it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -318,11 +331,8 @@ void GLGizmoMeasure::on_render() auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - const Transform3d view_model_matrix = view_matrix * feature_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * feature_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_cylinder.model.set_color(color); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(feature_color); m_cylinder.model.render(); auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -333,11 +343,8 @@ void GLGizmoMeasure::on_render() { const auto& [idx, normal, pt] = m_curr_feature->get_plane(); assert(idx < m_plane_models_cache.size()); - const Transform3d view_model_matrix = view_matrix * model_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); - const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); - shader->set_uniform("view_normal_matrix", view_normal_matrix); - m_plane_models_cache[idx].set_color(color); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(feature_color); m_plane_models_cache[idx].render(); auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) @@ -346,6 +353,15 @@ void GLGizmoMeasure::on_render() } } + if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { + if (m_hover_id != POINT_ID) { + Transform3d matrix = model_matrix * Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(matrix); + m_sphere.model.set_color(EXTENDED_HOVER_COLOR); + m_sphere.model.render(); + } + } + shader->stop_using(); } } @@ -450,70 +466,97 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - if (!m_curr_feature.has_value()) - m_imgui->text(_u8L("Select features to measure")); - else { - auto add_row_to_table = [this](const wxString& label, const std::string& value) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); - ImGui::TableSetColumnIndex(1); - m_imgui->text(value); - }; - auto format_double = [](double value) { - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); - }; - auto format_vec3 = [](const Vec3d& v) { - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); - }; + auto add_row_to_table = [this](const wxString& label, const std::string& value) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + ImGui::TableSetColumnIndex(1); + m_imgui->text(value); + }; + auto format_double = [](double value) { + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); + }; + auto format_vec3 = [](const Vec3d& v) { + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); + }; + if (ImGui::BeginTable("Commands", 2)) { + add_row_to_table(_u8L("Left mouse button"), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + if (m_mode == EMode::BasicSelection && m_hover_id != -1) + add_row_to_table(CTRL_STR, _u8L("Enable point selection")); + ImGui::EndTable(); + } + + if (m_curr_feature.has_value()) { const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - const Measure::SurfaceFeatureType type = m_curr_feature->get_type(); - if (type != Measure::SurfaceFeatureType::Undef) { - if (ImGui::CollapsingHeader(surface_feature_type_as_string(type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - switch (type) - { - case Measure::SurfaceFeatureType::Point: - { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); + const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); + if (m_mode == EMode::BasicSelection) { + if (feature_type != Measure::SurfaceFeatureType::Undef) { + if (ImGui::CollapsingHeader(surface_feature_type_as_string(feature_type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + if (ImGui::BeginTable("Data", 2)) { + switch (feature_type) + { + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * m_curr_feature->get_point(); + add_row_to_table(_u8L("Position") + ":", format_vec3(position)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + add_row_to_table(_u8L("From") + ":", format_vec3(from)); + add_row_to_table(_u8L("To") + ":", format_vec3(to)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Center") + ":", format_vec3(center)); + add_row_to_table(_u8L("Radius") + ":", format_double(radius)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); + add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); + break; + } + } + ImGui::EndTable(); + } + } + } + } + else if (m_mode == EMode::ExtendedSelection) { + if (m_hover_id != -1) { + std::string header; + switch (feature_type) { + case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { header = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { header = (m_hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { header = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + + if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { + const Vec3d position = volume_matrix * *m_curr_ex_feature_position; + if (ImGui::BeginTable("Data", 2)) { add_row_to_table(_u8L("Position") + ":", format_vec3(position)); - break; + ImGui::EndTable(); } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; - add_row_to_table(_u8L("From") + ":", format_vec3(from)); - add_row_to_table(_u8L("To") + ":", format_vec3(to)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Center") + ":", format_vec3(center)); - add_row_to_table(_u8L("Radius") + ":", format_double(radius)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - } - ImGui::EndTable(); } } } @@ -526,18 +569,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->set_requires_extra_frame(); } - if (m_curr_feature.has_value() && m_mode == EMode::BasicSelection) { - static const std::string ctrl = -#ifdef __APPLE__ - "⌘" -#else - "Ctrl" -#endif //__APPLE__ - ; - - ImGui::Separator(); - m_imgui->text(_u8L("Press") + " " + ctrl + " " + _u8L("to enable extended selection")); - } m_imgui->end(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 12e0fbfe1d..0c90d3d310 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -42,6 +42,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; + std::optional m_curr_ex_feature_position; // This holds information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 16aac5f9193df572b2f5c2dba89dfd4e2e680c99 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 14:56:00 +0200 Subject: [PATCH 218/327] Measuring: Measure gizmo - Improved visualization of points for extended selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 53 +++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 932f9113f1..89ba8cd410 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -257,8 +257,50 @@ void GLGizmoMeasure::on_render() } } } - else if (m_mode == EMode::ExtendedSelection) - m_curr_ex_feature_position = position_on_model.cast(); + else if (is_hovering_on_extended_selection) { + switch (m_curr_feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + m_curr_ex_feature_position = trafo * p.cast(); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + m_curr_ex_feature_position = model_matrix * position_on_model.cast(); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = m_curr_feature->get_circle(); + if (m_hover_id == POINT_ID) + m_curr_ex_feature_position = model_matrix * center; + else { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + m_curr_ex_feature_position = trafo * p.cast(); + } + } + break; + } + } + } if (!m_curr_feature.has_value()) return; @@ -355,7 +397,7 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - Transform3d matrix = model_matrix * Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); @@ -541,7 +583,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1) { + if (m_hover_id != -1 && m_curr_ex_feature_position.has_value()) { std::string header; switch (feature_type) { case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } @@ -552,9 +594,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - const Vec3d position = volume_matrix * *m_curr_ex_feature_position; if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position") + ":", format_vec3(position)); + add_row_to_table(_u8L("Position") + ":", format_vec3(*m_curr_ex_feature_position)); ImGui::EndTable(); } } From 8d923bb12ee61a02e0813f576860357602fd38ed Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 31 Aug 2022 15:31:39 +0200 Subject: [PATCH 219/327] Measuring: Measure gizmo - Further improvements in visualization of points for extended selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 89ba8cd410..a9c2e18257 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -273,6 +273,7 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + p = { 0.0f, 0.0f, p.z() }; m_curr_ex_feature_position = trafo * p.cast(); } break; @@ -294,6 +295,10 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); + float angle = std::atan2(p.y(), p.x()); + if (angle < 0.0f) + angle += 2.0f * float(M_PI); + p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); m_curr_ex_feature_position = trafo * p.cast(); } } From b64686835b9a72b53a26faaf3ca7908331a40ad0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 09:44:40 +0200 Subject: [PATCH 220/327] Measuring: Optimization into GLGizmoMeasure::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 59 +++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a9c2e18257..13415e70ac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -106,6 +106,9 @@ void GLGizmoMeasure::data_changed() } if (model_object != m_old_model_object || model_volume != m_old_model_volume) update_if_needed(); + + m_last_inv_zoom = 0.0f; + m_last_plane_idx = -1; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -211,12 +214,13 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = m_curr_feature->get_circle(); - // TODO: check for changed inv_zoom - - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); - m_circle.model.init_from(std::move(circle_geometry)); + if (m_last_inv_zoom != inv_zoom) { + m_last_inv_zoom = inv_zoom; + m_circle.reset(); + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.model.init_from(std::move(circle_geometry)); + } m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); @@ -225,32 +229,33 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Plane: { const auto& [idx, normal, point] = m_curr_feature->get_plane(); + if (m_last_plane_idx != idx) { + m_last_plane_idx = idx; + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + const std::vector& triangle_indices = planes_triangles[idx]; - // TODO: check for changed idx + const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; + m_plane.reset(); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.model.init_from(std::move(init_data)); } - m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); - m_plane.model.init_from(std::move(init_data)); m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) }); break; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 0c90d3d310..a19b2d5486 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -44,11 +44,13 @@ class GLGizmoMeasure : public GLGizmoBase std::optional m_curr_feature; std::optional m_curr_ex_feature_position; - // This holds information to decide whether recalculation is necessary: + // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; std::vector m_volumes_types; Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; + float m_last_inv_zoom{ 0.0f }; + int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo const ModelObject* m_old_model_object{ nullptr }; From 19df0dadeb49fb9f456609ae8fd52bbc779410bd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 09:54:16 +0200 Subject: [PATCH 221/327] Fixed warnings --- src/libslic3r/Measure.cpp | 18 +++++++++--------- src/libslic3r/Measure.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index cdde276dda..1a79369a3e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -106,7 +106,7 @@ void MeasuringImpl::update_planes() int facet_idx = facet_queue[-- facet_queue_cnt]; const stl_normal& this_normal = face_normals[facet_idx]; if (is_same_normal(this_normal, *normal_ptr)) { - const Vec3i& face = m_its.indices[facet_idx]; +// const Vec3i& face = m_its.indices[facet_idx]; m_face_to_plane[facet_idx] = m_planes.size() - 1; m_planes.back().facets.emplace_back(facet_idx); @@ -132,7 +132,7 @@ void MeasuringImpl::update_planes() for (int face_id=0; face_id lengths; - for (int i=0; i M_PI) @@ -236,10 +236,10 @@ void MeasuringImpl::extract_features() bool circle = false; std::vector circles; std::vector> circles_idxs; - for (int i=1; i polygon_lower_threshold) { if (angle < polygon_upper_threshold) { const Vec3d center = std::get<0>(circles[i].get_circle()); - for (int j=circles_idxs[i].first + 1; j<=circles_idxs[i].second; ++j) + for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[j-1], border[j], std::make_optional(center), 0.)); } else { @@ -286,7 +286,7 @@ void MeasuringImpl::extract_features() // We have the circles. Now go around again and pick edges. int cidx = 0; // index of next circle in the way for (int i=1; i circles_idxs[cidx].first) + if (cidx < (int)circles_idxs.size() && i > (int)circles_idxs[cidx].first) i = circles_idxs[cidx++].second; else plane.surface_features.emplace_back(SurfaceFeature( SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 95d8f34cba..9e3629589b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -68,7 +68,7 @@ private: Vec3d m_pt1; Vec3d m_pt2; std::optional m_pt3; - double m_value; + double m_value{ 0.0 }; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 13415e70ac..54c944590b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -200,6 +200,7 @@ void GLGizmoMeasure::on_render() return; switch (m_curr_feature->get_type()) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); @@ -265,6 +266,7 @@ void GLGizmoMeasure::on_render() else if (is_hovering_on_extended_selection) { switch (m_curr_feature->get_type()) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); @@ -342,7 +344,9 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - switch (m_curr_feature->get_type()) { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d& position = m_curr_feature->get_point(); @@ -552,6 +556,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Data", 2)) { switch (feature_type) { + default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d position = volume_matrix * m_curr_feature->get_point(); From 89ae6cf4ee792f3326db9967deba439ca6ecd9b3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 1 Sep 2022 10:19:12 +0200 Subject: [PATCH 222/327] Refactoring into GLGizmoMeasure.cpp to remove duplicated code --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 63 +++++++++++------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 54c944590b..3b257076a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -48,6 +48,30 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) +{ + assert(0 <= idx && idx < (int)planes_triangles.size()); + const std::vector& triangle_indices = planes_triangles[idx]; + + GLModel::Geometry init_data; + init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + unsigned int i = 0; + for (int idx : triangle_indices) { + const Vec3f& v0 = its.vertices[its.indices[idx][0]]; + const Vec3f& v1 = its.vertices[its.indices[idx][1]]; + const Vec3f& v2 = its.vertices[its.indices[idx][2]]; + + const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); + init_data.add_vertex(v0, n); + init_data.add_vertex(v1, n); + init_data.add_vertex(v2, n); + init_data.add_triangle(i, i + 1, i + 2); + i += 3; + } + + return init_data; +} + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -232,26 +256,9 @@ void GLGizmoMeasure::on_render() const auto& [idx, normal, point] = m_curr_feature->get_plane(); if (m_last_plane_idx != idx) { m_last_plane_idx = idx; - const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - const std::vector& triangle_indices = planes_triangles[idx]; - const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } - + const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane.reset(); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); m_plane.model.init_from(std::move(init_data)); @@ -427,23 +434,9 @@ void GLGizmoMeasure::update_if_needed() auto update_plane_models_cache = [this](const indexed_triangle_set& its) { m_plane_models_cache.clear(); const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); - for (const std::vector& triangle_indices : planes_triangles) { + for (int idx = 0; idx < (int)planes_triangles.size(); ++idx) { m_plane_models_cache.emplace_back(GLModel()); - GLModel::Geometry init_data; - init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - unsigned int i = 0; - for (int idx : triangle_indices) { - const Vec3f& v0 = its.vertices[its.indices[idx][0]]; - const Vec3f& v1 = its.vertices[its.indices[idx][1]]; - const Vec3f& v2 = its.vertices[its.indices[idx][2]]; - - const Vec3f n = (v1 - v0).cross(v2 - v0).normalized(); - init_data.add_vertex(v0, n); - init_data.add_vertex(v1, n); - init_data.add_vertex(v2, n); - init_data.add_triangle(i, i + 1, i + 2); - i += 3; - } + GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane_models_cache.back().init_from(std::move(init_data)); } }; From 928a642eb94ae8f04aee26e8242deb836cc1d3c3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 2 Sep 2022 11:24:06 +0200 Subject: [PATCH 223/327] Measuring: Added features selection in GLGizmoMeasure --- src/libslic3r/Measure.hpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 382 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 39 ++- 3 files changed, 297 insertions(+), 130 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 9e3629589b..3aefc0b54b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -107,7 +107,11 @@ struct MeasurementResult { std::optional angle; std::optional distance_infinite; std::optional distance_strict; - std::optional distance_xyz; + std::optional distance_xyz; + + bool has_any_data() const { + return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); + } }; // Returns distance/angle between two SurfaceFeatures. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3b257076a2..8c27678ce0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -31,9 +31,8 @@ std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType t namespace Slic3r { namespace GUI { -static const Slic3r::ColorRGBA BASIC_HOVER_COLOR = { 0.8f, 0.2f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA EXTENDED_HOVER_COLOR = { 0.8f, 0.8f, 0.2f, 1.0f }; -static const Slic3r::ColorRGBA LOCK_COLOR = { 0.5f, 0.5f, 0.5f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; +static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; static const int POINT_ID = 100; static const int EDGE_ID = 200; @@ -95,7 +94,23 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { - m_mouse_left_down = true; + m_mouse_left_down = true; + + auto set_item_from_feature = [this]() { + const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_ex_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : + m_curr_feature }; + return item; + }; + + if (m_selected_features.first.feature.has_value()) { + const SelectedFeatures::Item item = set_item_from_feature(); + if (m_selected_features.first != item) + m_selected_features.second = item; + } + else + m_selected_features.first = set_item_from_feature(); + return true; } @@ -103,7 +118,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) // take responsibility for left up if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true; - } else if (mouse_event.LeftUp()) { if (m_mouse_left_down) { @@ -133,6 +147,7 @@ void GLGizmoMeasure::data_changed() m_last_inv_zoom = 0.0f; m_last_plane_idx = -1; + m_selected_features.reset(); } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -321,13 +336,13 @@ void GLGizmoMeasure::on_render() } } - if (!m_curr_feature.has_value()) + if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) return; GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; - + shader->start_using(); shader->set_uniform("emission_factor", 0.25f); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); @@ -337,13 +352,6 @@ void GLGizmoMeasure::on_render() const Transform3d& view_matrix = camera.get_view_matrix(); - ColorRGBA feature_color; - switch (m_mode) - { - case EMode::BasicSelection: { feature_color = BASIC_HOVER_COLOR; break; } - case EMode::ExtendedSelection: { feature_color = LOCK_COLOR; break; } - } - auto set_matrix_uniforms = [shader, &view_matrix](const Transform3d& model_matrix) { const Transform3d view_model_matrix = view_matrix * model_matrix; shader->set_uniform("view_model_matrix", view_model_matrix); @@ -351,76 +359,143 @@ void GLGizmoMeasure::on_render() shader->set_uniform("view_normal_matrix", view_normal_matrix); }; - switch (m_curr_feature->get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = m_curr_feature->get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - m_sphere.model.set_color(is_hovering_on_extended_selection ? EXTENDED_HOVER_COLOR : feature_color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, n] = m_curr_feature->get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color((is_hovering_on_extended_selection && m_hover_id == POINT_ID) ? EXTENDED_HOVER_COLOR : feature_color); - m_sphere.model.render(); - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); + auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, + const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, n] = feature.get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = feature.get_edge(); + auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(colors.front()); + m_cylinder.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + } + break; + } + } + }; - // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); - set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(feature_color); - m_circle.model.render(); - it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - break; + auto hover_selection_color = [this]() { + return saturate(!m_selected_features.first.feature.has_value() ? SELECTED_1ST_COLOR : SELECTED_2ND_COLOR, 1.5f); + }; + + auto hovering_color = [this, hover_selection_color, &selection]() { + return (m_mode == EMode::ExtendedSelection) ? selection.get_first_volume()->render_color : hover_selection_color(); + }; + + if (m_curr_feature.has_value()) { + std::vector colors; + if (m_selected_features.first.feature.has_value() && *m_curr_feature == *m_selected_features.first.feature) + colors.emplace_back(SELECTED_1ST_COLOR); + else if (m_selected_features.second.feature.has_value() && *m_curr_feature == *m_selected_features.second.feature) + colors.emplace_back(SELECTED_2ND_COLOR); + else { + switch (m_curr_feature->get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + colors.emplace_back(hover_selection_color()); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + colors.emplace_back((m_hover_id == POINT_ID) ? hover_selection_color() : hovering_color()); + colors.emplace_back(hovering_color()); + break; + } + case Measure::SurfaceFeatureType::Edge: + case Measure::SurfaceFeatureType::Plane: + { + colors.emplace_back(hovering_color()); + break; + } + } + } + + render_feature(*m_curr_feature, colors, model_matrix, inv_zoom, true); } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = m_curr_feature->get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(feature_color); - m_cylinder.model.render(); - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = m_curr_feature->get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); - m_plane_models_cache[idx].set_color(feature_color); - m_plane_models_cache[idx].render(); - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); - break; + + if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { + std::vector colors; + colors.emplace_back(SELECTED_1ST_COLOR); + render_feature(*m_selected_features.first.feature, colors, + (m_selected_features.first.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } + if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { + std::vector colors; + colors.emplace_back(SELECTED_2ND_COLOR); + render_feature(*m_selected_features.second.feature, colors, + (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); - m_sphere.model.set_color(EXTENDED_HOVER_COLOR); + m_sphere.model.set_color(hover_selection_color()); +// m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); } } @@ -496,6 +571,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit { static std::optional last_feature; static EMode last_mode = EMode::BasicSelection; + static SelectedFeatures last_selected_features; static float last_y = 0.0f; static float last_h = 0.0f; @@ -515,18 +591,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const wxString& label, const std::string& value) { + auto add_row_to_table = [this](const std::string& label, const ImVec4& label_color, const std::string& value, const ImVec4& value_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, label); + m_imgui->text_colored(label_color, label); ImGui::TableSetColumnIndex(1); - m_imgui->text(value); + m_imgui->text_colored(value_color, value); }; + auto format_double = [](double value) { char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); }; + auto format_vec3 = [](const Vec3d& v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); @@ -534,9 +612,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (ImGui::BeginTable("Commands", 2)) { - add_row_to_table(_u8L("Left mouse button"), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + add_row_to_table(_u8L("Left mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, + (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_row_to_table(CTRL_STR, _u8L("Enable point selection")); + add_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -545,48 +624,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); if (m_mode == EMode::BasicSelection) { if (feature_type != Measure::SurfaceFeatureType::Undef) { - if (ImGui::CollapsingHeader(surface_feature_type_as_string(feature_type).c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - switch (feature_type) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); - add_row_to_table(_u8L("Position") + ":", format_vec3(position)); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; - add_row_to_table(_u8L("From") + ":", format_vec3(from)); - add_row_to_table(_u8L("To") + ":", format_vec3(to)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Center") + ":", format_vec3(center)); - add_row_to_table(_u8L("Radius") + ":", format_double(radius)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - add_row_to_table(_u8L("Origin") + ":", format_vec3(origin)); - add_row_to_table(_u8L("Normal") + ":", format_vec3(normal)); - break; - } - } - ImGui::EndTable(); + ImGui::Separator(); + m_imgui->text(surface_feature_type_as_string(feature_type) + ":"); + if (ImGui::BeginTable("Data", 2)) { + switch (feature_type) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d position = volume_matrix * m_curr_feature->get_point(); + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = volume_matrix * from; + to = volume_matrix * to; + add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Length") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = volume_matrix * center; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Radius") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = volume_matrix * origin; + normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + ImGui::EndTable(); } } } @@ -601,20 +681,68 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } } - if (ImGui::CollapsingHeader(header.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { - if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position") + ":", format_vec3(*m_curr_ex_feature_position)); - ImGui::EndTable(); - } + ImGui::Separator(); + m_imgui->text(header + ":"); + if (ImGui::BeginTable("Data", 2)) { + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_ex_feature_position), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::EndTable(); } } } } - if (last_feature != m_curr_feature || last_mode != m_mode) { + ImGui::Separator(); + m_imgui->text(_u8L("Selection")); + if (ImGui::BeginTable("Selection", 2)) { + add_row_to_table("1", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + surface_feature_type_as_string(m_selected_features.first.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + add_row_to_table("2", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + surface_feature_type_as_string(m_selected_features.second.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + ImGui::EndTable(); + } + + if (m_selected_features.first.feature.has_value()) { + if (m_imgui->button(_u8L("Restart"))) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); + } + } + + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + ImGui::Separator(); + if (measure.has_any_data()) { + m_imgui->text(_u8L("Measure")); + if (ImGui::BeginTable("Measure", 2)) { + if (measure.angle.has_value()) { + add_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_infinite.has_value()) { + add_row_to_table(_u8L("Distance Infinite") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_infinite), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_strict.has_value()) { + add_row_to_table(_u8L("Distance Strict") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_strict), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + if (measure.distance_xyz.has_value()) { + add_row_to_table(_u8L("Distance XYZ") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*measure.distance_xyz), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } + } + else + m_imgui->text(_u8L("No measure available")); + } + + if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { // the dialog may have changed its size, ask for an extra frame to render it properly last_feature = m_curr_feature; last_mode = m_mode; + last_selected_features = m_selected_features; m_imgui->set_requires_extra_frame(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index a19b2d5486..28e617e4cd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -23,14 +23,47 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoMeasure : public GLGizmoBase { -// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. - enum class EMode : unsigned char { BasicSelection, ExtendedSelection }; + struct SelectedFeatures + { + struct Item + { + EMode mode{ EMode::BasicSelection }; + std::optional feature; + + bool operator == (const Item& other) const { + if (this->mode != other.mode) return false; + return this->feature == other.feature; + } + + bool operator != (const Item& other) const { + return !operator == (other); + } + }; + + Item first; + Item second; + + void reset() { + first.feature.reset(); + second.feature.reset(); + } + + bool operator == (const SelectedFeatures& other) const { + if (this->first != other.first) return false; + return this->second == other.second; + } + + bool operator != (const SelectedFeatures& other) const { + return !operator == (other); + } + }; + EMode m_mode{ EMode::BasicSelection }; std::unique_ptr m_measuring; @@ -60,6 +93,8 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; + SelectedFeatures m_selected_features; + void update_if_needed(); public: From 1e5b01a31d3085c8edd06a6b6c4eb291b3c98edf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 2 Sep 2022 12:26:56 +0200 Subject: [PATCH 224/327] Measuring: Changes in GLGizmoMeasure imgui dialog layout --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 97 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 14 +++- 2 files changed, 61 insertions(+), 50 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8c27678ce0..6fe6c36ef3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -15,19 +15,6 @@ #if ENABLE_MEASURE_GIZMO -std::string surface_feature_type_as_string(Slic3r::Measure::SurfaceFeatureType type) -{ - switch (type) - { - default: - case Slic3r::Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Slic3r::Measure::SurfaceFeatureType::Point: { return L("Vertex"); } - case Slic3r::Measure::SurfaceFeatureType::Edge: { return L("Edge"); } - case Slic3r::Measure::SurfaceFeatureType::Circle: { return L("Circle"); } - case Slic3r::Measure::SurfaceFeatureType::Plane: { return L("Plane"); } - } -} - namespace Slic3r { namespace GUI { @@ -47,6 +34,32 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) +{ + switch (type) + { + default: + case Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } + case Measure::SurfaceFeatureType::Point: { return L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + } +} + +static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id) +{ + std::string ret; + switch (type) { + case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } + case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } + default: { assert(false); break; } + } + return ret; +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -97,9 +110,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto set_item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_ex_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : - m_curr_feature }; + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_point_on_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : m_curr_feature }; return item; }; @@ -179,7 +192,7 @@ void GLGizmoMeasure::on_set_state() if (m_state == Off) { m_ctrl_kar_filter.reset_count(); m_curr_feature.reset(); - m_curr_ex_feature_position.reset(); + m_curr_point_on_feature_position.reset(); } else m_mode = EMode::BasicSelection; @@ -231,7 +244,7 @@ void GLGizmoMeasure::on_render() curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); if (m_mode == EMode::BasicSelection) { - m_curr_ex_feature_position.reset(); + m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); m_curr_feature = curr_feature; @@ -291,7 +304,7 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_ex_feature_position = model_matrix * m_curr_feature->get_point(); + m_curr_point_on_feature_position = model_matrix * m_curr_feature->get_point(); break; } case Measure::SurfaceFeatureType::Edge: @@ -303,20 +316,20 @@ void GLGizmoMeasure::on_render() const Transform3d& trafo = it->second->get_transform(); it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); p = { 0.0f, 0.0f, p.z() }; - m_curr_ex_feature_position = trafo * p.cast(); + m_curr_point_on_feature_position = trafo * p.cast(); } break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_ex_feature_position = model_matrix * position_on_model.cast(); + m_curr_point_on_feature_position = model_matrix * position_on_model.cast(); break; } case Measure::SurfaceFeatureType::Circle: { const auto& [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) - m_curr_ex_feature_position = model_matrix * center; + m_curr_point_on_feature_position = model_matrix * center; else { auto it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) { @@ -328,7 +341,7 @@ void GLGizmoMeasure::on_render() if (angle < 0.0f) angle += 2.0f * float(M_PI); p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); - m_curr_ex_feature_position = trafo * p.cast(); + m_curr_point_on_feature_position = trafo * p.cast(); } } break; @@ -490,12 +503,11 @@ void GLGizmoMeasure::on_render() (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } - if (is_hovering_on_extended_selection && m_curr_ex_feature_position.has_value()) { + if (is_hovering_on_extended_selection && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = Geometry::translation_transform(*m_curr_ex_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); -// m_sphere.model.set_color(EXTENDED_HOVER_COLOR); m_sphere.model.render(); } } @@ -591,12 +603,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const std::string& label, const ImVec4& label_color, const std::string& value, const ImVec4& value_color) { + auto add_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(label_color, label); + m_imgui->text_colored(col_1_color, col_1); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(value_color, value); + m_imgui->text_colored(col_2_color, col_2); }; auto format_double = [](double value) { @@ -671,20 +683,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } } else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1 && m_curr_ex_feature_position.has_value()) { - std::string header; - switch (feature_type) { - case Measure::SurfaceFeatureType::Point: { header = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { header = _u8L("Point on edge"); break; } - case Measure::SurfaceFeatureType::Circle: { header = (m_hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } - case Measure::SurfaceFeatureType::Plane: { header = _u8L("Point on plane"); break; } - default: { assert(false); break; } - } - + if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { ImGui::Separator(); - m_imgui->text(header + ":"); + m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_ex_feature_position), + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_point_on_feature_position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -693,12 +696,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ImGui::Separator(); - m_imgui->text(_u8L("Selection")); - if (ImGui::BeginTable("Selection", 2)) { - add_row_to_table("1", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? - surface_feature_type_as_string(m_selected_features.first.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_row_to_table("2", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? - surface_feature_type_as_string(m_selected_features.second.feature->get_type()) : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); + const ImGuiTableFlags flags = /*ImGuiTableFlags_SizingStretchSame | */ImGuiTableFlags_BordersOuter | /*ImGuiTableFlags_BordersV | */ImGuiTableFlags_BordersH; + if (ImGui::BeginTable("Selection", 2, flags)) { + add_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); + add_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 28e617e4cd..4492080732 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -34,24 +34,32 @@ class GLGizmoMeasure : public GLGizmoBase struct Item { EMode mode{ EMode::BasicSelection }; + std::string source; std::optional feature; bool operator == (const Item& other) const { if (this->mode != other.mode) return false; + if (this->source != other.source) return false; return this->feature == other.feature; } bool operator != (const Item& other) const { return !operator == (other); } + + void reset() { + mode = EMode::BasicSelection; + source.clear(); + feature.reset(); + } }; Item first; Item second; void reset() { - first.feature.reset(); - second.feature.reset(); + first.reset(); + second.reset(); } bool operator == (const SelectedFeatures& other) const { @@ -75,7 +83,7 @@ class GLGizmoMeasure : public GLGizmoBase std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; - std::optional m_curr_ex_feature_position; + std::optional m_curr_point_on_feature_position; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; From 9edc2545cec19b6ed52d95e427239e18840e5d13 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 08:26:19 +0200 Subject: [PATCH 225/327] Measuring: Disable scene raycasters while GLGizmoMeasure is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 101 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 5 ++ 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6fe6c36ef3..8fddf11adf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -167,8 +167,10 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po { if (action == SLAGizmoEventType::CtrlDown) { if (m_ctrl_kar_filter.is_first()) { - if (m_curr_feature.has_value()) + if (m_curr_feature.has_value()) { m_mode = EMode::ExtendedSelection; + disable_scene_raycasters(); + } } m_ctrl_kar_filter.increase_count(); @@ -176,6 +178,7 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po else if (action == SLAGizmoEventType::CtrlUp) { m_ctrl_kar_filter.reset_count(); m_mode = EMode::BasicSelection; + restore_scene_raycasters_state(); } return true; @@ -193,9 +196,19 @@ void GLGizmoMeasure::on_set_state() m_ctrl_kar_filter.reset_count(); m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); + restore_scene_raycasters_state(); } - else + else { m_mode = EMode::BasicSelection; + // store current state of scene raycaster for later use + m_scene_raycaster_state.clear(); + m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (m_scene_raycasters != nullptr) { + for (const auto r : *m_scene_raycasters) { + m_scene_raycaster_state.emplace_back(r->is_active()); + } + } + } } CommonGizmosDataID GLGizmoMeasure::on_get_requirements() const @@ -234,16 +247,13 @@ void GLGizmoMeasure::on_render() Vec3f position_on_model; Vec3f normal_on_model; - size_t facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &facet_idx); + size_t model_facet_idx; + m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - const bool is_hovering_on_extended_selection = m_mode == EMode::ExtendedSelection && m_hover_id != -1; - - std::optional curr_feature; - if (m_mode == EMode::BasicSelection) - curr_feature = m_measuring->get_feature(facet_idx, position_on_model.cast()); + const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { + std::optional curr_feature = m_measuring->get_feature(model_facet_idx, position_on_model.cast()); m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); @@ -265,8 +275,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, normal] = m_curr_feature->get_circle(); - + const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_last_inv_zoom != inv_zoom) { m_last_inv_zoom = inv_zoom; m_circle.reset(); @@ -281,7 +290,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Plane: { - const auto& [idx, normal, point] = m_curr_feature->get_plane(); + const auto [idx, normal, point] = m_curr_feature->get_plane(); if (m_last_plane_idx != idx) { m_last_plane_idx = idx; const indexed_triangle_set its = (m_old_model_volume != nullptr) ? m_old_model_volume->mesh().its : m_old_model_object->volumes.front()->mesh().its; @@ -298,7 +307,23 @@ void GLGizmoMeasure::on_render() } } } - else if (is_hovering_on_extended_selection) { + else if (is_hovering_on_locked_feature) { + auto default_callback = [](const Vec3f& v) { return v; }; + auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { + auto it = m_raycasters.find(feature_type_id); + if (it != m_raycasters.end() && it->second != nullptr) { + Vec3f p; + Vec3f n; + const Transform3d& trafo = it->second->get_transform(); + bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); + assert(res); + if (callback) + p = callback(p); + return trafo * p.cast(); + } + return Vec3d::Zero(); + }; + switch (m_curr_feature->get_type()) { default: { assert(false); break; } @@ -309,40 +334,27 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) { - Vec3f p; - Vec3f n; - const Transform3d& trafo = it->second->get_transform(); - it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); - p = { 0.0f, 0.0f, p.z() }; - m_curr_point_on_feature_position = trafo * p.cast(); - } + m_curr_point_on_feature_position = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = model_matrix * position_on_model.cast(); + m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); break; } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, normal] = m_curr_feature->get_circle(); + const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) m_curr_point_on_feature_position = model_matrix * center; else { - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) { - Vec3f p; - Vec3f n; - const Transform3d& trafo = it->second->get_transform(); - it->second->get_raycaster()->unproject_on_mesh(m_mouse_pos, trafo, camera, p, n); - float angle = std::atan2(p.y(), p.x()); + const float r = radius; // needed for the following lambda + m_curr_point_on_feature_position = position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { + float angle = std::atan2(v.y(), v.x()); if (angle < 0.0f) angle += 2.0f * float(M_PI); - p = float(radius) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); - m_curr_point_on_feature_position = trafo * p.cast(); - } + return float(r) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); + }); } break; } @@ -503,7 +515,7 @@ void GLGizmoMeasure::on_render() (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); } - if (is_hovering_on_extended_selection && m_curr_point_on_feature_position.has_value()) { + if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); @@ -579,6 +591,25 @@ void GLGizmoMeasure::update_if_needed() } } +void GLGizmoMeasure::disable_scene_raycasters() +{ + if (m_scene_raycasters != nullptr) { + for (auto r : *m_scene_raycasters) { + r->set_active(false); + } + } +} + +void GLGizmoMeasure::restore_scene_raycasters_state() +{ + if (m_scene_raycasters != nullptr) { + assert(m_scene_raycasters->size() == m_scene_raycaster_state.size()); + for (size_t i = 0; i < m_scene_raycasters->size(); ++i) { + (*m_scene_raycasters)[i]->set_active(m_scene_raycaster_state[i]); + } + } +} + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 4492080732..9dad613973 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -84,6 +84,8 @@ class GLGizmoMeasure : public GLGizmoBase std::map> m_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; + std::vector>* m_scene_raycasters{ nullptr }; + std::vector m_scene_raycaster_state; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; @@ -105,6 +107,9 @@ class GLGizmoMeasure : public GLGizmoBase void update_if_needed(); + void disable_scene_raycasters(); + void restore_scene_raycasters_state(); + public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 9a7fd520b145e0743e9185ecaf7fe445084a4c1b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 08:57:49 +0200 Subject: [PATCH 226/327] Fixed warnings --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8fddf11adf..74a5c93f7c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -308,7 +308,6 @@ void GLGizmoMeasure::on_render() } } else if (is_hovering_on_locked_feature) { - auto default_callback = [](const Vec3f& v) { return v; }; auto position_on_feature = [this](int feature_type_id, const Camera& camera, std::function callback = nullptr) -> Vec3d { auto it = m_raycasters.find(feature_type_id); if (it != m_raycasters.end() && it->second != nullptr) { @@ -317,9 +316,11 @@ void GLGizmoMeasure::on_render() const Transform3d& trafo = it->second->get_transform(); bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); assert(res); - if (callback) - p = callback(p); - return trafo * p.cast(); + if (res) { + if (callback) + p = callback(p); + return trafo * p.cast(); + } } return Vec3d::Zero(); }; From ed7534b8ceb136c96f5680eb8a445f175fd0f58b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 09:18:07 +0200 Subject: [PATCH 227/327] Measuring: GLGizmoMeasure - Fixed update of circle geometry --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 74a5c93f7c..bf7fd04a7d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -276,8 +276,9 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Circle: { const auto [center, radius, normal] = m_curr_feature->get_circle(); - if (m_last_inv_zoom != inv_zoom) { + if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { m_last_inv_zoom = inv_zoom; + m_last_circle = m_curr_feature; m_circle.reset(); GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9dad613973..7c82f7b9f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -93,6 +93,7 @@ class GLGizmoMeasure : public GLGizmoBase Vec3d m_first_instance_scale{ Vec3d::Ones() }; Vec3d m_first_instance_mirror{ Vec3d::Ones() }; float m_last_inv_zoom{ 0.0f }; + std::optional m_last_circle; int m_last_plane_idx{ -1 }; bool m_mouse_left_down{ false }; // for detection left_up of this gizmo From 7b8c07c2a4c59e80c88f7d04a298286b7ecbb793 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 12:05:25 +0200 Subject: [PATCH 228/327] Measuring: GLGizmoMeasure - show data in inches into imgui dialog, when needed --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 42 ++++++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bf7fd04a7d..f8f84d7bc8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -3,6 +3,7 @@ #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectManipulation.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" @@ -664,6 +665,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::EndTable(); } + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); + if (m_curr_feature.has_value()) { const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); @@ -677,7 +681,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - const Vec3d position = volume_matrix * m_curr_feature->get_point(); + Vec3d position = volume_matrix * m_curr_feature->get_point(); + if (use_inches) + position = ObjectManipulation::mm_to_in * position; add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -686,9 +692,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [from, to] = m_curr_feature->get_edge(); from = volume_matrix * from; to = volume_matrix * to; + if (use_inches) { + from = ObjectManipulation::mm_to_in * from; + to = ObjectManipulation::mm_to_in * to; + } add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Length") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -696,8 +706,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [center, radius, normal] = m_curr_feature->get_circle(); center = volume_matrix * center; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) { + center = ObjectManipulation::mm_to_in * center; + radius = ObjectManipulation::mm_to_in * radius; + } add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Radius") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -706,6 +720,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit auto [idx, normal, origin] = m_curr_feature->get_plane(); origin = volume_matrix * origin; normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) + origin = ObjectManipulation::mm_to_in * origin; add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; @@ -720,7 +736,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*m_curr_point_on_feature_position), + Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -756,15 +775,24 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { - add_row_to_table(_u8L("Distance Infinite") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_infinite), + double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { - add_row_to_table(_u8L("Distance Strict") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(*measure.distance_strict), + double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { - add_row_to_table(_u8L("Distance XYZ") + _u8L(" (mm)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*measure.distance_xyz), + Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + add_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); From 1971f0b2cb5ac45367bca457743c2ae956732f5c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 12:49:19 +0200 Subject: [PATCH 229/327] Measuring: GLGizmoMeasure - Fixed detection of current hovered feature --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f8f84d7bc8..40ca6d3df3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -249,12 +249,11 @@ void GLGizmoMeasure::on_render() Vec3f position_on_model; Vec3f normal_on_model; size_t model_facet_idx; - m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); - + const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { - std::optional curr_feature = m_measuring->get_feature(model_facet_idx, position_on_model.cast()); + std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { GLGizmoMeasure::on_unregister_raycasters_for_picking(); @@ -552,7 +551,7 @@ void GLGizmoMeasure::update_if_needed() // Let's save what we calculated it from: m_volumes_matrices.clear(); m_volumes_types.clear(); - m_first_instance_scale = Vec3d::Ones(); + m_first_instance_scale = Vec3d::Ones(); m_first_instance_mirror = Vec3d::Ones(); if (object != nullptr) { for (const ModelVolume* vol : object->volumes) { From f95014dce2c39acf602618dc1ba349861a0da5fd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 13:26:01 +0200 Subject: [PATCH 230/327] Fixed warnings on ARM64 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 40ca6d3df3..c1e81ff21d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,11 +89,11 @@ GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filen : GLGizmoBase(parent, icon_filename, sprite_id) { GLModel::Geometry sphere_geometry = smooth_sphere(16, 7.5f); - m_sphere.mesh_raycaster = std::make_unique(std::make_shared(std::move(sphere_geometry.get_as_indexed_triangle_set()))); + m_sphere.mesh_raycaster = std::make_unique(std::make_shared(sphere_geometry.get_as_indexed_triangle_set())); m_sphere.model.init_from(std::move(sphere_geometry)); GLModel::Geometry cylinder_geometry = smooth_cylinder(16, 5.0f, 1.0f); - m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(std::move(cylinder_geometry.get_as_indexed_triangle_set()))); + m_cylinder.mesh_raycaster = std::make_unique(std::make_shared(cylinder_geometry.get_as_indexed_triangle_set())); m_cylinder.model.init_from(std::move(cylinder_geometry)); } @@ -205,7 +205,7 @@ void GLGizmoMeasure::on_set_state() m_scene_raycaster_state.clear(); m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); if (m_scene_raycasters != nullptr) { - for (const auto r : *m_scene_raycasters) { + for (const auto& r : *m_scene_raycasters) { m_scene_raycaster_state.emplace_back(r->is_active()); } } @@ -281,7 +281,7 @@ void GLGizmoMeasure::on_render() m_last_circle = m_curr_feature; m_circle.reset(); GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(std::move(circle_geometry.get_as_indexed_triangle_set()))); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); m_circle.model.init_from(std::move(circle_geometry)); } @@ -298,7 +298,7 @@ void GLGizmoMeasure::on_render() const std::vector> planes_triangles = m_measuring->get_planes_triangle_indices(); GLModel::Geometry init_data = init_plane_data(its, planes_triangles, idx); m_plane.reset(); - m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(init_data.get_as_indexed_triangle_set()))); + m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); m_plane.model.init_from(std::move(init_data)); } From e10bd47ba315cefaca7883830119e2ccc570c3dd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 5 Sep 2022 15:16:31 +0200 Subject: [PATCH 231/327] Measuring: GLGizmoMeasure - Added colored icon into imgui dialog --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 68 +++++++++++++++--------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c1e81ff21d..21e86532a1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -636,12 +636,20 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + auto add_row_to_table = [this](std::function col_1 = nullptr, std::function col_2 = nullptr) { + assert(col_1 != nullptr && col_2 != nullptr); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - m_imgui->text_colored(col_1_color, col_1); + col_1(); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(col_2_color, col_2); + col_2(); + }; + + auto add_strings_row_to_table = [this, add_row_to_table](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + add_row_to_table( + [this, &col_1, &col_1_color]() { m_imgui->text_colored(col_1_color, col_1); }, + [this, &col_2, &col_2_color]() { m_imgui->text_colored(col_2_color, col_2); } + ); }; auto format_double = [](double value) { @@ -657,10 +665,22 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (ImGui::BeginTable("Commands", 2)) { - add_row_to_table(_u8L("Left mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, - (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_row_to_table( + [this]() { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); + }, + [this]() { + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + ImGui::SameLine(); + const ImVec2 pos = ImGui::GetCursorScreenPos(); + const float rect_size = ImGui::GetTextLineHeight(); + ImGui::GetWindowDrawList()->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + rect_size, pos.y + rect_size }, + ImGuiWrapper::to_ImU32(m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR)); + ImGui::Dummy(ImVec2(rect_size, rect_size)); + } + ); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -683,7 +703,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d position = volume_matrix * m_curr_feature->get_point(); if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: @@ -695,9 +715,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit from = ObjectManipulation::mm_to_in * from; to = ObjectManipulation::mm_to_in * to; } - add_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -709,9 +729,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; } - add_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Plane: @@ -721,8 +741,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) origin = ObjectManipulation::mm_to_in * origin; - add_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } } @@ -738,7 +758,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), + add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -747,11 +767,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ImGui::Separator(); - const ImGuiTableFlags flags = /*ImGuiTableFlags_SizingStretchSame | */ImGuiTableFlags_BordersOuter | /*ImGuiTableFlags_BordersV | */ImGuiTableFlags_BordersH; + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; if (ImGui::BeginTable("Selection", 2, flags)) { - add_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + add_strings_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + add_strings_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } @@ -767,31 +787,31 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); ImGui::Separator(); if (measure.has_any_data()) { - m_imgui->text(_u8L("Measure")); + m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 2)) { if (measure.angle.has_value()) { - add_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_strings_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_strings_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); From 512073d4894f37f4739453284891ff7cdbfc5d96 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 10:17:53 +0200 Subject: [PATCH 232/327] Measuring: Added missing default values to SurfaceFeature member variables --- src/libslic3r/Measure.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 3aefc0b54b..3b811db0c5 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -64,9 +64,9 @@ public: } private: - SurfaceFeatureType m_type = SurfaceFeatureType::Undef; - Vec3d m_pt1; - Vec3d m_pt2; + SurfaceFeatureType m_type{ SurfaceFeatureType::Undef }; + Vec3d m_pt1{ Vec3d::Zero() }; + Vec3d m_pt2{ Vec3d::Zero() }; std::optional m_pt3; double m_value{ 0.0 }; }; From d9cb8919511fbf12431fa5fa7e87e5969353a4da Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 10:54:56 +0200 Subject: [PATCH 233/327] Measuring: Rewritten method SurfaceFeature::operator ==() --- src/libslic3r/Measure.hpp | 23 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 3b811db0c5..5d71983f0b 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -44,19 +44,28 @@ public: std::tuple get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); } // For planes, return index into vector provided by Measuring::get_plane_triangle_indices, normal and point. - std::tuple get_plane() const { return std::make_tuple(int(m_value), m_pt1, m_pt2); } + std::tuple get_plane() const { assert(m_type == SurfaceFeatureType::Plane); return std::make_tuple(int(m_value), m_pt1, m_pt2); } // For anything, return an extra point that should also be considered a part of this. std::optional get_extra_point() const { assert(m_type != SurfaceFeatureType::Undef); return m_pt3; } bool operator == (const SurfaceFeature& other) const { if (this->m_type != other.m_type) return false; - if (!this->m_pt1.isApprox(other.m_pt1)) return false; - if (!this->m_pt2.isApprox(other.m_pt2)) return false; - if (this->m_pt3.has_value() && !other.m_pt3.has_value()) return false; - if (!this->m_pt3.has_value() && other.m_pt3.has_value()) return false; - if (this->m_pt3.has_value() && other.m_pt3.has_value() && !(*this->m_pt3).isApprox(*other.m_pt3)) return false; - return this->m_value == other.m_value; + switch (this->m_type) + { + case SurfaceFeatureType::Undef: { break; } + case SurfaceFeatureType::Point: { return (this->m_pt1.isApprox(other.m_pt1)); } + case SurfaceFeatureType::Edge: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2)) || + (this->m_pt1.isApprox(other.m_pt2) && this->m_pt2.isApprox(other.m_pt1)); + } + case SurfaceFeatureType::Plane: + case SurfaceFeatureType::Circle: { + return (this->m_pt1.isApprox(other.m_pt1) && this->m_pt2.isApprox(other.m_pt2) && std::abs(this->m_value - other.m_value) < EPSILON); + } + } + + return false; } bool operator != (const SurfaceFeature& other) const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 21e86532a1..942a8c93eb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -113,7 +113,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) auto set_item_from_feature = [this]() { const SelectedFeatures::Item item = { m_mode, (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(Measure::SurfaceFeatureType::Point, *m_curr_point_on_feature_position, Vec3d::Zero(), std::nullopt, 0.0) : m_curr_feature }; + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; return item; }; @@ -331,7 +331,7 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_point_on_feature_position = model_matrix * m_curr_feature->get_point(); + m_curr_point_on_feature_position = position_on_feature(POINT_ID, camera); break; } case Measure::SurfaceFeatureType::Edge: From bedfffac39c459b158708f31ba2455c1b9e3878c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 12:02:44 +0200 Subject: [PATCH 234/327] Measuring: GLGizmoMeasure - Use mouse right click to restart selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 49 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 942a8c93eb..ebf3139868 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -110,20 +110,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_hover_id != -1) { m_mouse_left_down = true; - auto set_item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; - return item; - }; - if (m_selected_features.first.feature.has_value()) { - const SelectedFeatures::Item item = set_item_from_feature(); + const SelectedFeatures::Item item = item_from_feature(); if (m_selected_features.first != item) m_selected_features.second = item; } else - m_selected_features.first = set_item_from_feature(); + m_selected_features.first = item_from_feature(); return true; } @@ -140,6 +133,16 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return true; } } + else if (mouse_event.RightDown()) { + if (m_hover_id != -1) { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first == item_from_feature()) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); + } + } + } + } else if (mouse_event.Leaving()) m_mouse_left_down = false; @@ -612,6 +615,14 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +GLGizmoMeasure::SelectedFeatures::Item GLGizmoMeasure::item_from_feature() const +{ + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + return item; +} + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -679,6 +690,14 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Dummy(ImVec2(rect_size, rect_size)); } ); + + if (m_hover_id != -1) { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first == item_from_feature()) + add_strings_row_to_table(_u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + } + if (m_mode == EMode::BasicSelection && m_hover_id != -1) add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); @@ -776,12 +795,12 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::EndTable(); } - if (m_selected_features.first.feature.has_value()) { - if (m_imgui->button(_u8L("Restart"))) { - m_selected_features.reset(); - m_imgui->set_requires_extra_frame(); - } - } + //if (m_selected_features.first.feature.has_value()) { + // if (m_imgui->button(_u8L("Restart"))) { + // m_selected_features.reset(); + // m_imgui->set_requires_extra_frame(); + // } + //} if (m_selected_features.second.feature.has_value()) { const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 7c82f7b9f8..d415de2c9c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -110,6 +110,7 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); + SelectedFeatures::Item item_from_feature() const; public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 805a793f53c4245c90302bf168f59e578a0d4e0b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 6 Sep 2022 15:06:56 +0200 Subject: [PATCH 235/327] Follow-up of 2b7520dc9eee8063c7d376811603c2d09c4a75e4 - Use CTRL + mouse right click to restart selection --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++---------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 - 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index ebf3139868..f1a011e572 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -109,6 +109,13 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { m_mouse_left_down = true; + + auto item_from_feature = [this]() { + const SelectedFeatures::Item item = { m_mode, + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + return item; + }; if (m_selected_features.first.feature.has_value()) { const SelectedFeatures::Item item = item_from_feature(); @@ -133,15 +140,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) return true; } } - else if (mouse_event.RightDown()) { - if (m_hover_id != -1) { - if (m_selected_features.first.feature.has_value()) { - if (m_selected_features.first == item_from_feature()) { - m_selected_features.reset(); - m_imgui->set_requires_extra_frame(); - } - } - } + else if (mouse_event.RightDown() && mouse_event.CmdDown()) { + m_selected_features.reset(); + m_imgui->set_requires_extra_frame(); } else if (mouse_event.Leaving()) m_mouse_left_down = false; @@ -615,14 +616,6 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } -GLGizmoMeasure::SelectedFeatures::Item GLGizmoMeasure::item_from_feature() const -{ - const SelectedFeatures::Item item = { m_mode, - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; - return item; -} - void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -691,12 +684,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); - if (m_hover_id != -1) { - if (m_selected_features.first.feature.has_value()) { - if (m_selected_features.first == item_from_feature()) - add_strings_row_to_table(_u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - } - } + if (m_selected_features.first.feature.has_value()) + add_strings_row_to_table(CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index d415de2c9c..7c82f7b9f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -110,7 +110,6 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); - SelectedFeatures::Item item_from_feature() const; public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 14224eb38e848438acc4e722c0cbf0da65a48a57 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Sep 2022 13:07:15 +0200 Subject: [PATCH 236/327] Measuring: bunch of fixes into GLGizmoMeasure + new tech ENABLE_MEASURE_GIZMO_DEBUG to show a debug imgui dialog containing data related to Measure Gizmo --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 222 +++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 12 +- 3 files changed, 152 insertions(+), 83 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a96a39066c..04ecfae670 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -68,6 +68,7 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) +#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f1a011e572..b434cea9a7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -111,7 +111,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto item_from_feature = [this]() { - const SelectedFeatures::Item item = { m_mode, + const SelectedFeatures::Item item = { (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; return item; @@ -236,6 +236,10 @@ bool GLGizmoMeasure::on_is_activable() const void GLGizmoMeasure::on_render() { +#if ENABLE_MEASURE_GIZMO_DEBUG + render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + // do not render if the user is panning/rotating the 3d scene if (m_parent.is_mouse_dragging()) return; @@ -246,14 +250,14 @@ void GLGizmoMeasure::on_render() (selection.is_single_volume() || selection.is_single_volume_instance())) { update_if_needed(); - const Transform3d& model_matrix = selection.get_first_volume()->world_matrix(); + m_volume_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); Vec3f position_on_model; Vec3f normal_on_model; size_t model_facet_idx; - const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, model_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); + const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; if (m_mode == EMode::BasicSelection) { @@ -335,31 +339,31 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - m_curr_point_on_feature_position = position_on_feature(POINT_ID, camera); + m_curr_point_on_feature_position = m_curr_feature->get_point(); break; } case Measure::SurfaceFeatureType::Edge: { - m_curr_point_on_feature_position = position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: { - m_curr_point_on_feature_position = position_on_feature(PLANE_ID, camera); + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(PLANE_ID, camera); break; } case Measure::SurfaceFeatureType::Circle: { const auto [center, radius, normal] = m_curr_feature->get_circle(); if (m_hover_id == POINT_ID) - m_curr_point_on_feature_position = model_matrix * center; + m_curr_point_on_feature_position = center; else { const float r = radius; // needed for the following lambda - m_curr_point_on_feature_position = position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { float angle = std::atan2(v.y(), v.x()); if (angle < 0.0f) angle += 2.0f * float(M_PI); - return float(r) * Vec3f(std::cos(angle), std::sin(angle), 0.0f); + return Vec3f(float(r) * std::cos(angle), float(r) * std::sin(angle), 0.0f); }); } break; @@ -411,7 +415,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto& [center, radius, n] = feature.get_circle(); + const auto& [center, radius, normal] = feature.get_circle(); // render center const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); @@ -423,7 +427,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center); + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); m_circle.model.set_color(colors.back()); m_circle.model.render(); @@ -437,8 +441,8 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const auto& [start, end] = feature.get_edge(); - auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * q * + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); set_matrix_uniforms(feature_matrix); m_cylinder.model.set_color(colors.front()); @@ -505,25 +509,23 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, model_matrix, inv_zoom, true); + render_feature(*m_curr_feature, colors, m_volume_matrix, inv_zoom, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); - render_feature(*m_selected_features.first.feature, colors, - (m_selected_features.first.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); - render_feature(*m_selected_features.second.feature, colors, - (m_selected_features.second.mode == EMode::BasicSelection) ? model_matrix : Transform3d::Identity(), inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); } if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -616,6 +618,100 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) +{ + assert(col_1 != nullptr && col_2 != nullptr); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + col_1(); + ImGui::TableSetColumnIndex(1); + col_2(); +}; + +static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) +{ + add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); +}; + +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +}; + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +}; + +#if ENABLE_MEASURE_GIZMO_DEBUG +void GLGizmoMeasure::render_debug_dialog() +{ + auto add_feature_data = [this](const SelectedFeatures::Item& item) { + add_strings_row_to_table(*m_imgui, "Type", ImGuiWrapper::COL_ORANGE_LIGHT, item.source, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + switch (item.feature->get_type()) + { + case Measure::SurfaceFeatureType::Point: + { + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(item.feature->get_point()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = item.feature->get_edge(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = item.feature->get_plane(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(idx), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = item.feature->get_circle(); + add_strings_row_to_table(*m_imgui, "m_pt1", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_pt2", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, "m_value", ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + break; + } + } + std::optional extra_point = item.feature->get_extra_point(); + if (extra_point.has_value()) + add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + }; + + m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) + m_imgui->text("Empty selection"); + else { + const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; + if (m_selected_features.first.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Selection 1"); + if (ImGui::BeginTable("Selection 1", 2, flags)) { + add_feature_data(m_selected_features.first); + ImGui::EndTable(); + } + } + if (m_selected_features.second.feature.has_value()) { + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Selection 2"); + if (ImGui::BeginTable("Selection 2", 2, flags)) { + add_feature_data(m_selected_features.second); + ImGui::EndTable(); + } + } + } + m_imgui->end(); +} +#endif // ENABLE_MEASURE_GIZMO_DEBUG + void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit) { static std::optional last_feature; @@ -640,34 +736,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit last_y = y; } - auto add_row_to_table = [this](std::function col_1 = nullptr, std::function col_2 = nullptr) { - assert(col_1 != nullptr && col_2 != nullptr); - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - col_1(); - ImGui::TableSetColumnIndex(1); - col_2(); - }; - - auto add_strings_row_to_table = [this, add_row_to_table](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { - add_row_to_table( - [this, &col_1, &col_1_color]() { m_imgui->text_colored(col_1_color, col_1); }, - [this, &col_2, &col_2_color]() { m_imgui->text_colored(col_2_color, col_2); } - ); - }; - - auto format_double = [](double value) { - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); - }; - - auto format_vec3 = [](const Vec3d& v) { - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); - }; - if (ImGui::BeginTable("Commands", 2)) { add_row_to_table( [this]() { @@ -685,10 +753,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ); if (m_selected_features.first.feature.has_value()) - add_strings_row_to_table(CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); if (m_mode == EMode::BasicSelection && m_hover_id != -1) - add_strings_row_to_table(CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } @@ -696,7 +764,6 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); if (m_curr_feature.has_value()) { - const Transform3d volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); if (m_mode == EMode::BasicSelection) { if (feature_type != Measure::SurfaceFeatureType::Undef) { @@ -708,49 +775,49 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - Vec3d position = volume_matrix * m_curr_feature->get_point(); + Vec3d position = m_volume_matrix * m_curr_feature->get_point(); if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Edge: { auto [from, to] = m_curr_feature->get_edge(); - from = volume_matrix * from; - to = volume_matrix * to; + from = m_volume_matrix * from; + to = m_volume_matrix * to; if (use_inches) { from = ObjectManipulation::mm_to_in * from; to = ObjectManipulation::mm_to_in * to; } - add_strings_row_to_table(_u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: { auto [center, radius, normal] = m_curr_feature->get_circle(); - center = volume_matrix * center; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + center = m_volume_matrix * center; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) { center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; } - add_strings_row_to_table(_u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Plane: { auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = volume_matrix * origin; - normal = volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; if (use_inches) origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(_u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(_u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } } @@ -763,11 +830,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); if (ImGui::BeginTable("Data", 2)) { - Vec3d position = ObjectManipulation::mm_to_in * *m_curr_point_on_feature_position; + Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; if (use_inches) position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(_u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::EndTable(); } } @@ -777,9 +843,9 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; if (ImGui::BeginTable("Selection", 2, flags)) { - add_strings_row_to_table(_u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? + add_strings_row_to_table(*m_imgui, _u8L("Selection") + " 1:", ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR), m_selected_features.first.feature.has_value() ? m_selected_features.first.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_1ST_COLOR)); - add_strings_row_to_table(_u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? + add_strings_row_to_table(*m_imgui, _u8L("Selection") + " 2:", ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR), m_selected_features.second.feature.has_value() ? m_selected_features.second.source : _u8L("None"), ImGuiWrapper::to_ImVec4(SELECTED_2ND_COLOR)); ImGui::EndTable(); } @@ -798,28 +864,28 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 2)) { if (measure.angle.has_value()) { - add_strings_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_strings_row_to_table(*m_imgui, _u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_infinite.has_value()) { - double distance = ObjectManipulation::mm_to_in * *measure.distance_infinite; + double distance = *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_strict.has_value()) { - double distance = ObjectManipulation::mm_to_in * *measure.distance_strict; + double distance = *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } if (measure.distance_xyz.has_value()) { - Vec3d distance = ObjectManipulation::mm_to_in * *measure.distance_xyz; + Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_strings_row_to_table(*m_imgui, _u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); } ImGui::EndTable(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 7c82f7b9f8..e0789c821f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -33,12 +33,10 @@ class GLGizmoMeasure : public GLGizmoBase { struct Item { - EMode mode{ EMode::BasicSelection }; std::string source; std::optional feature; bool operator == (const Item& other) const { - if (this->mode != other.mode) return false; if (this->source != other.source) return false; return this->feature == other.feature; } @@ -48,7 +46,6 @@ class GLGizmoMeasure : public GLGizmoBase } void reset() { - mode = EMode::BasicSelection; source.clear(); feature.reset(); } @@ -62,12 +59,12 @@ class GLGizmoMeasure : public GLGizmoBase second.reset(); } - bool operator == (const SelectedFeatures& other) const { + bool operator == (const SelectedFeatures & other) const { if (this->first != other.first) return false; return this->second == other.second; } - bool operator != (const SelectedFeatures& other) const { + bool operator != (const SelectedFeatures & other) const { return !operator == (other); } }; @@ -80,6 +77,7 @@ class GLGizmoMeasure : public GLGizmoBase PickingModel m_circle; PickingModel m_plane; + Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::optional m_curr_feature; @@ -111,6 +109,10 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); +#if ENABLE_MEASURE_GIZMO_DEBUG + void render_debug_dialog(); +#endif // ENABLE_MEASURE_GIZMO_DEBUG + public: GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); From 950272aff1b28d5e952e795215be544845375efd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 7 Sep 2022 14:04:18 +0200 Subject: [PATCH 237/327] Measuring: Gizmo measure disabled for sinking volumes --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b434cea9a7..db9fc0dd34 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -229,9 +229,12 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? + bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); + if (res) + res &= !selection.get_first_volume()->is_sinking(); + return res; } void GLGizmoMeasure::on_render() From f9087d580082167a1732f66bfd44aa1d5d540020 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 12:28:16 +0200 Subject: [PATCH 238/327] Measuring: Gizmo measure shows dimensioning for distance point-point --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 146 +++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 8 ++ 2 files changed, 154 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db9fc0dd34..92046c299f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -27,6 +27,9 @@ static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; +static const float TRIANGLE_BASE = 16.0f; +static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; + static const std::string CTRL_STR = #ifdef __APPLE__ "⌘" @@ -537,6 +540,8 @@ void GLGizmoMeasure::on_render() shader->stop_using(); } + + render_dimensioning(); } void GLGizmoMeasure::update_if_needed() @@ -621,6 +626,147 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +void GLGizmoMeasure::render_dimensioning() +{ + if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) + return; + + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; + + auto point_point_2D = [this, shader](const Vec3d& v1, const Vec3d& v2) { + if (v1.isApprox(v2)) + return; + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& view_matrix = camera.get_view_matrix(); + const Transform3d& projection_matrix = camera.get_projection_matrix(); + const Matrix4d projection_view_matrix = projection_matrix.matrix() * view_matrix.matrix(); + const std::array& viewport = camera.get_viewport(); + const double inv_zoom = camera.get_inv_zoom(); + + auto model_to_world = [this](const Vec3d& model) { + return (Vec3d)(m_volume_matrix * model); + }; + + auto world_to_clip = [&projection_view_matrix](const Vec3d& world) { + return (Vec4d)(projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0)); + }; + + auto clip_to_ndc = [](const Vec4d& clip) { + return Vec2d(clip.x(), clip.y()) / clip.w(); + }; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + + auto ndc_to_ss = [&viewport, half_w, half_h](const Vec2d& ndc) { + return Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h); + }; + + Matrix4d ndc_to_ss_matrix; + ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + const Transform3d ss_to_ndc_matrix = Transform3d(ndc_to_ss_matrix.inverse()); + + // ndc coordinates + const Vec2d v1ndc = clip_to_ndc(world_to_clip(model_to_world(v1))); + const Vec2d v2ndc = clip_to_ndc(world_to_clip(model_to_world(v2))); + const Vec2d v12ndc = v2ndc - v1ndc; + const double v12ndc_len = v12ndc.norm(); + + // screen coordinates + const Vec2d v1ss = ndc_to_ss(v1ndc); + const Vec2d v2ss = ndc_to_ss(v2ndc); + + if (v1ss.isApprox(v2ss)) + return; + + const Vec2d v12ss = v2ss - v1ss; + const double v12ss_len = v12ss.norm(); + + const bool overlap = v12ss_len - 2.0 * TRIANGLE_HEIGHT < 0.0; + + const auto q12ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(v12ss.x(), v12ss.y(), 0.0)); + const auto q21ss = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(-v12ss.x(), -v12ss.y(), 0.0)); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; + const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + + // stem + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::scale_transform({ v12ss_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + + // arrow 1 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss : + ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q21ss); + m_dimensioning.triangle.render(); + + // arrow 2 + shader->set_uniform("view_model_matrix", overlap ? + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : + ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); + m_dimensioning.triangle.render(); + }; + + shader->start_using(); + + if (!m_dimensioning.line.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(2); + init_data.reserve_indices(2); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(1.0f, 0.0f, 0.0f)); + + // indices + init_data.add_line(0, 1); + + m_dimensioning.line.init_from(std::move(init_data)); + } + + if (!m_dimensioning.triangle.is_initialized()) { + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(3); + init_data.reserve_indices(3); + + // vertices + init_data.add_vertex(Vec3f(0.0f, 0.0f, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, 0.5f * TRIANGLE_BASE, 0.0f)); + init_data.add_vertex(Vec3f(-TRIANGLE_HEIGHT, -0.5f * TRIANGLE_BASE, 0.0f)); + + // indices + init_data.add_triangle(0, 1, 2); + + m_dimensioning.triangle.init_from(std::move(init_data)); + } + + glsafe(::glDisable(GL_DEPTH_TEST)); + + if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_point_2D(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + } + + glsafe(::glEnable(GL_DEPTH_TEST)); + + shader->stop_using(); +} + static void add_row_to_table(std::function col_1 = nullptr, std::function col_2 = nullptr) { assert(col_1 != nullptr && col_2 != nullptr); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index e0789c821f..4417a0d608 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -76,6 +76,12 @@ class GLGizmoMeasure : public GLGizmoBase PickingModel m_cylinder; PickingModel m_circle; PickingModel m_plane; + struct Dimensioning + { + GLModel line; + GLModel triangle; + }; + Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; @@ -109,6 +115,8 @@ class GLGizmoMeasure : public GLGizmoBase void disable_scene_raycasters(); void restore_scene_raycasters_state(); + void render_dimensioning(); + #if ENABLE_MEASURE_GIZMO_DEBUG void render_debug_dialog(); #endif // ENABLE_MEASURE_GIZMO_DEBUG From 5825e850128f54dbc675f3f9b2bf12fe18dc60c3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 14:42:04 +0200 Subject: [PATCH 239/327] Measuring: Gizmo measure shows dimensioning for distance point-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 166 +++++++++++++++++------ 1 file changed, 125 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 92046c299f..4caae10f43 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -626,6 +626,79 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } +class DimensioningHelper +{ + struct Cache + { + std::array viewport; + Matrix4d ndc_to_ss_matrix; + Transform3d ndc_to_ss_matrix_inverse; + }; + + static Cache s_cache; + +public: + static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { + return world_matrix * model; + } + + 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 Vec3d clip_to_ndc(const Vec4d& clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + } + + 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 Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); + } + + static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); + } + + static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); + } + + static const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix; + } + + static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix_inverse; + } + +private: + static void update(const std::array& viewport) { + if (s_cache.viewport == viewport) + return; + + std::cout << "DimensioningHelper::update()\n"; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); + s_cache.viewport = viewport; + } +}; + +DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; + void GLGizmoMeasure::render_dimensioning() { if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) @@ -635,53 +708,17 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point_2D = [this, shader](const Vec3d& v1, const Vec3d& v2) { + auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2) { if (v1.isApprox(v2)) return; const Camera& camera = wxGetApp().plater()->get_camera(); - const Transform3d& view_matrix = camera.get_view_matrix(); - const Transform3d& projection_matrix = camera.get_projection_matrix(); - const Matrix4d projection_view_matrix = projection_matrix.matrix() * view_matrix.matrix(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); - const double inv_zoom = camera.get_inv_zoom(); - - auto model_to_world = [this](const Vec3d& model) { - return (Vec3d)(m_volume_matrix * model); - }; - - auto world_to_clip = [&projection_view_matrix](const Vec3d& world) { - return (Vec4d)(projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0)); - }; - - auto clip_to_ndc = [](const Vec4d& clip) { - return Vec2d(clip.x(), clip.y()) / clip.w(); - }; - - const double half_w = 0.5 * double(viewport[2]); - const double half_h = 0.5 * double(viewport[3]); - - auto ndc_to_ss = [&viewport, half_w, half_h](const Vec2d& ndc) { - return Vec2d(half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h); - }; - - Matrix4d ndc_to_ss_matrix; - ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, - 0.0, half_h, 0.0, double(viewport[1]) + half_h, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0; - - const Transform3d ss_to_ndc_matrix = Transform3d(ndc_to_ss_matrix.inverse()); - - // ndc coordinates - const Vec2d v1ndc = clip_to_ndc(world_to_clip(model_to_world(v1))); - const Vec2d v2ndc = clip_to_ndc(world_to_clip(model_to_world(v2))); - const Vec2d v12ndc = v2ndc - v1ndc; - const double v12ndc_len = v12ndc.norm(); // screen coordinates - const Vec2d v1ss = ndc_to_ss(v1ndc); - const Vec2d v2ss = ndc_to_ss(v2ndc); + const Vec2d v1ss = DimensioningHelper::model_to_ss(v1, m_volume_matrix, projection_view_matrix, viewport); + const Vec2d v2ss = DimensioningHelper::model_to_ss(v2, m_volume_matrix, projection_view_matrix, viewport); if (v1ss.isApprox(v2ss)) return; @@ -699,6 +736,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; + const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + // stem shader->set_uniform("view_model_matrix", overlap ? ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : @@ -718,6 +757,43 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; + auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { + const Vec3d e1v = v - e.first; + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1e2_unit = e1e2.normalized(); + const Vec3d v_proj_on_e1e2 = e.first + e1v.dot(e1e2_unit) * e1e2_unit; + point_point(v, v_proj_on_e1e2); + + const Vec3d v_proj_on_e1e2e1 = v_proj_on_e1e2 - e.first; + bool on_e1_side = v_proj_on_e1e2e1.dot(e1e2) < 0.0; + bool on_e2_side = v_proj_on_e1e2e1.norm() > e1e2.norm(); + if (on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + const std::array& viewport = camera.get_viewport(); + const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + + shader->set_uniform("projection_matrix", Transform3d::Identity()); + + const Vec2d v_proj_on_e1e2ss = DimensioningHelper::model_to_ss(v_proj_on_e1e2, m_volume_matrix, projection_view_matrix, viewport); + auto render_extension = [this, &v_proj_on_e1e2ss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); + if (!pss.isApprox(v_proj_on_e1e2ss)) { + const Vec2d pv_proj_on_e1e2ss = v_proj_on_e1e2ss - pss; + const double pv_proj_on_e1e2ss_len = pv_proj_on_e1e2ss.norm(); + + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_proj_on_e1e2ss.x(), pv_proj_on_e1e2ss.y(), 0.0)); + + shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * + Geometry::scale_transform({ pv_proj_on_e1e2ss_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } + }; + + render_extension(on_e1_side ? e.first : e.second); + } + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -759,7 +835,15 @@ void GLGizmoMeasure::render_dimensioning() if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point_2D(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } glsafe(::glEnable(GL_DEPTH_TEST)); From 55209dba4b4d50c0964ecd4f61dd84e149c02f01 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 15 Sep 2022 15:27:49 +0200 Subject: [PATCH 240/327] Measuring: Gizmo measure shows dimensioning for distance point-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 45 ++++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4caae10f43..c1e6a0c9ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -758,34 +758,32 @@ void GLGizmoMeasure::render_dimensioning() }; auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { - const Vec3d e1v = v - e.first; const Vec3d e1e2 = e.second - e.first; const Vec3d e1e2_unit = e1e2.normalized(); - const Vec3d v_proj_on_e1e2 = e.first + e1v.dot(e1e2_unit) * e1e2_unit; - point_point(v, v_proj_on_e1e2); + const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; + point_point(v, v_proj); - const Vec3d v_proj_on_e1e2e1 = v_proj_on_e1e2 - e.first; - bool on_e1_side = v_proj_on_e1e2e1.dot(e1e2) < 0.0; - bool on_e2_side = v_proj_on_e1e2e1.norm() > e1e2.norm(); + const Vec3d v_proje1 = v_proj - e.first; + bool on_e1_side = v_proje1.dot(e1e2) < 0.0; + bool on_e2_side = v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); - shader->set_uniform("projection_matrix", Transform3d::Identity()); - - const Vec2d v_proj_on_e1e2ss = DimensioningHelper::model_to_ss(v_proj_on_e1e2, m_volume_matrix, projection_view_matrix, viewport); - auto render_extension = [this, &v_proj_on_e1e2ss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { + const Vec2d v_projss = DimensioningHelper::model_to_ss(v_proj, m_volume_matrix, projection_view_matrix, viewport); + auto render_extension = [this, &v_projss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); - if (!pss.isApprox(v_proj_on_e1e2ss)) { - const Vec2d pv_proj_on_e1e2ss = v_proj_on_e1e2ss - pss; - const double pv_proj_on_e1e2ss_len = pv_proj_on_e1e2ss.norm(); + if (!pss.isApprox(v_projss)) { + const Vec2d pv_projss = v_projss - pss; + const double pv_projss_len = pv_projss.norm(); - const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_proj_on_e1e2ss.x(), pv_proj_on_e1e2ss.y(), 0.0)); + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Vec3d(pv_projss.x(), pv_projss.y(), 0.0)); + shader->set_uniform("projection_matrix", Transform3d::Identity()); shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * - Geometry::scale_transform({ pv_proj_on_e1e2ss_len, 1.0f, 1.0f })); + Geometry::scale_transform({ pv_projss_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; @@ -794,6 +792,15 @@ void GLGizmoMeasure::render_dimensioning() } }; + auto point_plane = [this, shader, point_point](const Vec3d& v, const std::tuple& p) { + const auto& [idx, normal, origin] = p; + const double distance = normal.dot(v - origin); + if (std::abs(distance) < EPSILON) + return; + + point_point(v, v - distance * normal); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -845,6 +852,14 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); + } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From cf55ffbd5efb1dd5874b4ef9e23d71a24195d570 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 08:30:19 +0200 Subject: [PATCH 241/327] Measuring: Gizmo measure shows dimensioning for distance edge-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 39 +++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c1e6a0c9ff..d166982144 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -792,7 +792,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [this, shader, point_point](const Vec3d& v, const std::tuple& p) { + auto point_plane = [shader, point_point](const Vec3d& v, const std::tuple& p) { const auto& [idx, normal, origin] = p; const double distance = normal.dot(v - origin); if (std::abs(distance) < EPSILON) @@ -801,6 +801,39 @@ void GLGizmoMeasure::render_dimensioning() point_point(v, v - distance * normal); }; + auto edge_edge = [this, shader, point_point](const std::pair& e1, const std::pair& e2) { + auto min_distance_edge_edge = [](const std::pair& e1, const std::pair& e2) { + auto distance_point_edge = [](const Vec3d& v, const std::pair& e) { + const Vec3d e1v = v - e.first; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; + return std::make_pair((e1v - v_proj).norm(), v_proj); + }; + + std::vector> distances; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); + distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); + const auto dist_e11e2 = distance_point_edge(e1.first, e2); + distances.emplace_back(std::make_tuple(dist_e11e2.first, e1.first, dist_e11e2.second)); + const auto dist_e12e2 = distance_point_edge(e1.second, e2); + distances.emplace_back(std::make_tuple(dist_e12e2.first, e1.second, dist_e12e2.second)); + const auto dist_e21e1 = distance_point_edge(e2.first, e1); + distances.emplace_back(std::make_tuple(dist_e21e1.first, e2.first, dist_e21e1.second)); + const auto dist_e22e1 = distance_point_edge(e2.second, e1); + distances.emplace_back(std::make_tuple(dist_e22e1.first, e2.second, dist_e22e1.second)); + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); + }; + + const auto [dist, v1, v2] = min_distance_edge_edge(e1, e2); + point_point(v1, v2); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -860,6 +893,10 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); } + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From 124216da029c0b29062f87c7fd1636f1c2c249d9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 09:57:07 +0200 Subject: [PATCH 242/327] Measuring: Gizmo measure shows dimensioning for distance point-circle --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 122 ++++++++++++++--------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d166982144..bc89051520 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -19,6 +19,10 @@ namespace Slic3r { namespace GUI { +using Edge = std::pair; +using Plane = std::tuple; +using Circle = std::tuple; + static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; @@ -64,6 +68,46 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } +static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) +{ + const double distance = std::get<1>(p).dot(v - std::get<2>(p)); + return std::make_tuple(std::abs(distance), v, v - distance * std::get<1>(p)); +} + +static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) +{ + const Vec3d e1v = v - e.first; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d v_proj = e.first + e1e2_unit.dot(e1v) * e1e2_unit; + return std::make_tuple((e1v - v_proj).norm(), v, v_proj); +} + +static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) +{ + const auto& [center, radius, normal] = c; + const auto [distance, v1, v2] = distance_point_plane(v, std::make_tuple(0, normal, center)); + const Vec3d p_on_circle = center + radius * (v2 - center).normalized(); + return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); +} + +static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) +{ + std::vector> distances; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); + distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); + distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); + distances.emplace_back(distance_point_edge(e1.first, e2)); + distances.emplace_back(distance_point_edge(e1.second, e2)); + distances.emplace_back(distance_point_edge(e2.first, e1)); + distances.emplace_back(distance_point_edge(e2.second, e1)); + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -757,12 +801,11 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const std::pair& e) { - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1e2_unit = e1e2.normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; - point_point(v, v_proj); + auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { + const auto [distance, v1, v_proj] = distance_point_edge(v, e); + point_point(v1, v_proj); + const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; bool on_e1_side = v_proje1.dot(e1e2) < 0.0; bool on_e2_side = v_proje1.norm() > e1e2.norm(); @@ -792,45 +835,18 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [shader, point_point](const Vec3d& v, const std::tuple& p) { - const auto& [idx, normal, origin] = p; - const double distance = normal.dot(v - origin); - if (std::abs(distance) < EPSILON) - return; - - point_point(v, v - distance * normal); + auto point_plane = [point_point](const Vec3d& v, const Plane& p) { + const auto [distance, v1, v2] = distance_point_plane(v, p); + point_point(v1, v2); }; - auto edge_edge = [this, shader, point_point](const std::pair& e1, const std::pair& e2) { - auto min_distance_edge_edge = [](const std::pair& e1, const std::pair& e2) { - auto distance_point_edge = [](const Vec3d& v, const std::pair& e) { - const Vec3d e1v = v - e.first; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(v - e.first) * e1e2_unit; - return std::make_pair((e1v - v_proj).norm(), v_proj); - }; + auto point_circle = [point_point](const Vec3d& v, const Circle& c) { + const auto [distance, v1, v2] = distance_point_circle(v, c); + point_point(v1, v2); + }; - std::vector> distances; - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - const auto dist_e11e2 = distance_point_edge(e1.first, e2); - distances.emplace_back(std::make_tuple(dist_e11e2.first, e1.first, dist_e11e2.second)); - const auto dist_e12e2 = distance_point_edge(e1.second, e2); - distances.emplace_back(std::make_tuple(dist_e12e2.first, e1.second, dist_e12e2.second)); - const auto dist_e21e1 = distance_point_edge(e2.first, e1); - distances.emplace_back(std::make_tuple(dist_e21e1.first, e2.first, dist_e21e1.second)); - const auto dist_e22e1 = distance_point_edge(e2.second, e1); - distances.emplace_back(std::make_tuple(dist_e22e1.first, e2.second, dist_e22e1.second)); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); - }; - - const auto [dist, v1, v2] = min_distance_edge_edge(e1, e2); + auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { + const auto [distance, v1, v2] = min_distance_edge_edge(e1, e2); point_point(v1, v2); }; @@ -873,26 +889,42 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); + // point-point if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); } + // point-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); } - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } + // point-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); } + // point-circle + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); + } + // edge-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); + } + // plane-point else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); } + // circle-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); + } + // edge-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); From 0d70bbba8b94ada8d7efd6c9ba50e70afadcc485 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 11:24:44 +0200 Subject: [PATCH 243/327] Measuring: Use eigen library in distance calculations for Gizmo measure --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bc89051520..bfcef7365b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -70,23 +70,22 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) { - const double distance = std::get<1>(p).dot(v - std::get<2>(p)); - return std::make_tuple(std::abs(distance), v, v - distance * std::get<1>(p)); + const auto& [idx, normal, origin] = p; + const Eigen::Hyperplane plane(normal, origin); + return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); } static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) { - const Vec3d e1v = v - e.first; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - const Vec3d v_proj = e.first + e1e2_unit.dot(e1v) * e1e2_unit; - return std::make_tuple((e1v - v_proj).norm(), v, v_proj); + const Eigen::ParametrizedLine line(e.first, (e.second - e.first).normalized()); + return std::make_tuple(line.distance(v), v, line.projection(v)); } static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) { const auto& [center, radius, normal] = c; - const auto [distance, v1, v2] = distance_point_plane(v, std::make_tuple(0, normal, center)); - const Vec3d p_on_circle = center + radius * (v2 - center).normalized(); + const Eigen::Hyperplane plane(normal, center); + const Vec3d p_on_circle = center + radius * (plane.projection(v) - center).normalized(); return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } From a911df78eea8382307db22b8036b6d61cb4e0d2a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 16 Sep 2022 13:28:15 +0200 Subject: [PATCH 244/327] Measuring: Gizmo measure shows dimensioning for distance plane-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bfcef7365b..9e0d2f60f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,6 +89,14 @@ static std::tuple distance_point_circle(const Vec3d& v, co return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } +static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : + std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); +} + static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; @@ -726,8 +734,6 @@ private: if (s_cache.viewport == viewport) return; - std::cout << "DimensioningHelper::update()\n"; - const double half_w = 0.5 * double(viewport[2]); const double half_h = 0.5 * double(viewport[3]); s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, @@ -849,6 +855,11 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; + auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { + const auto [distance, v1, v2] = distance_plane_plane(p1, p2); + point_point(v1, v2); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -928,6 +939,11 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // plane-plane + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From c01270ca995ae04b5cc9bf6026c4585792a4fbb8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 10:01:02 +0200 Subject: [PATCH 245/327] Measuring: Gizmo measure shows dimensioning for distance edge-circle --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 62 +++++++++++++++++++----- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9e0d2f60f8..bdb0420a8c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -89,15 +89,7 @@ static std::tuple distance_point_circle(const Vec3d& v, co return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} - -static std::tuple min_distance_edge_edge(const Edge& e1, const Edge& e2) +static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); @@ -115,6 +107,37 @@ static std::tuple min_distance_edge_edge(const Edge& e1, c return distances.front(); } +static std::tuple distance_edge_circle(const Edge& e, const Circle& c) +{ + const auto& [center, radius, normal] = c; + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + double dot = std::abs(e1e2_unit.dot(normal)); + + if (dot < EPSILON) { + // edge parallel to circle's plane + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Vec3d inter = line.intersectionPoint(plane); + return distance_point_circle(inter, c); + } + else if (std::abs(dot - 1.0) < EPSILON) + // edge parallel to circle's normal + return distance_point_circle(e.first, c); + else { + const auto [distance1, v11, v12] = distance_point_circle(e.first, c); + const auto [distance2, v21, v22] = distance_point_circle(e.second, c); + return (distance1 <= distance2) ? std::make_tuple(distance1, v11, v12) : std::make_tuple(distance2, v21, v22); + } +} + +static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : + std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -812,8 +835,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; - bool on_e1_side = v_proje1.dot(e1e2) < 0.0; - bool on_e2_side = v_proje1.norm() > e1e2.norm(); + const bool on_e1_side = v_proje1.dot(e1e2) < 0.0; + const bool on_e2_side = v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); @@ -851,7 +874,12 @@ void GLGizmoMeasure::render_dimensioning() }; auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { - const auto [distance, v1, v2] = min_distance_edge_edge(e1, e2); + const auto [distance, v1, v2] = distance_edge_edge(e1, e2); + point_point(v1, v2); + }; + + auto edge_circle = [point_point](const Edge& e, const Circle& c) { + const auto [distance, v1, v2] = distance_edge_circle(e, c); point_point(v1, v2); }; @@ -939,11 +967,21 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // edge-circle + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { + edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); + } // plane-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } + // circle-edge + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); + } glsafe(::glEnable(GL_DEPTH_TEST)); From c5fd4d8a7db44deb31ebc2a0a5b9fbf5426c2cac Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 11:44:03 +0200 Subject: [PATCH 246/327] Follow-up of 81d28c545cc1bbb31ee618934ae3afc72f3fc668 - Distance edge-circle calculated as in Fusion 360 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index bdb0420a8c..89396071c7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -110,24 +110,25 @@ static std::tuple distance_edge_edge(const Edge& e1, const static std::tuple distance_edge_circle(const Edge& e, const Circle& c) { const auto& [center, radius, normal] = c; - const Vec3d e1e2_unit = (e.second - e.first).normalized(); - double dot = std::abs(e1e2_unit.dot(normal)); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = e1e2.normalized(); - if (dot < EPSILON) { - // edge parallel to circle's plane - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line(e.first, e1e2_unit); - const Vec3d inter = line.intersectionPoint(plane); - return distance_point_circle(inter, c); - } - else if (std::abs(dot - 1.0) < EPSILON) - // edge parallel to circle's normal - return distance_point_circle(e.first, c); - else { - const auto [distance1, v11, v12] = distance_point_circle(e.first, c); - const auto [distance2, v21, v22] = distance_point_circle(e.second, c); - return (distance1 <= distance2) ? std::make_tuple(distance1, v11, v12) : std::make_tuple(distance2, v21, v22); - } + std::vector> distances; + distances.emplace_back(distance_point_circle(e.first, c)); + distances.emplace_back(distance_point_circle(e.second, c)); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(distance_point_circle(inter, c)); + + std::sort(distances.begin(), distances.end(), + [](const std::tuple& item1, const std::tuple& item2) { + return std::get<0>(item1) < std::get<0>(item2); + }); + return distances.front(); } static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) From 3bc7418835227e64133fae16e9e0af5c4378ea79 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 19 Sep 2022 13:22:09 +0200 Subject: [PATCH 247/327] Follow-up of 8d98f0869dc6c6c6e3c67da4bb0670f17c7acf36 - Distance edge-edge calculated as in Fusion 360 --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 89396071c7..5a477034ea 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -92,14 +92,22 @@ static std::tuple distance_point_circle(const Vec3d& v, co static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; + auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { + const auto [distance, v1, v2] = distance_point_edge(v, e); + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(std::make_tuple(distance, v, v2)); + }; + distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - distances.emplace_back(distance_point_edge(e1.first, e2)); - distances.emplace_back(distance_point_edge(e1.second, e2)); - distances.emplace_back(distance_point_edge(e2.first, e1)); - distances.emplace_back(distance_point_edge(e2.second, e1)); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); std::sort(distances.begin(), distances.end(), [](const std::tuple& item1, const std::tuple& item2) { return std::get<0>(item1) < std::get<0>(item2); From e8a9280843c0dba1415d7150c9f982f6979d1cdd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 21 Sep 2022 09:16:49 +0200 Subject: [PATCH 248/327] Measuring: Gizmo measure shows dimensioning for angle edge-edge --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 135 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 3 +- 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5a477034ea..f6a6a46ca5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -31,7 +31,7 @@ static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; -static const float TRIANGLE_BASE = 16.0f; +static const float TRIANGLE_BASE = 10.0f; static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; static const std::string CTRL_STR = @@ -782,6 +782,8 @@ DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix void GLGizmoMeasure::render_dimensioning() { + static SelectedFeatures last_selected_features; + if (!m_selected_features.first.feature.has_value() || !m_selected_features.second.feature.has_value()) return; @@ -844,8 +846,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; - const bool on_e1_side = v_proje1.dot(e1e2) < 0.0; - const bool on_e2_side = v_proje1.norm() > e1e2.norm(); + const bool on_e1_side = v_proje1.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && v_proje1.norm() > e1e2.norm(); if (on_e1_side || on_e2_side) { const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); @@ -882,9 +884,131 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; - auto edge_edge = [point_point](const Edge& e1, const Edge& e2) { + auto arc_edge_edge = [this, shader](Edge e1, Edge e2) { + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + // edges are parallel, return + return; + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // center in world coordinate + const Vec3d center = qp.inverse() * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // revert edges, if needed (we want them to move away from the center) + unsigned int revert_count = 0; + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e1.first, e1.second); + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + ++revert_count; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e2.first, e2.second); + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + ++revert_count; + } + + if (revert_count == 1) { + normal = -normal; + qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + qp_inverse = qp.inverse(); + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + if (!m_dimensioning.arc.is_initialized()) { + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; + init_data.color = ColorRGBA::WHITE(); + init_data.reserve_vertices(resolution + 1); + init_data.reserve_indices(resolution + 1); + + // vertices + indices + const double step = angle / double(resolution); + for (unsigned int i = 0; i <= resolution; ++i) { + const double a = step * double(i); + const Vec3d v = radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + init_data.add_vertex((Vec3f)v.cast()); + init_data.add_index(i); + } + + m_dimensioning.arc.init_from(std::move(init_data)); + } + + auto render_edge_entension = [this, shader, ¢er, radius](const Edge& e, bool coplanar) { + const Vec3d e1center = center - e.first; + const Vec3d e1e2 = e.second - e.first; + const bool on_e1_side = e1center.dot(e1e2) < -EPSILON; + const bool on_e2_side = !on_e1_side && e1center.norm() > e1e2.norm(); + if (!coplanar || on_e1_side || on_e2_side) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + auto render_extension = [this, shader, &camera, ¢er, radius, coplanar](const Vec3d& p) { + const Vec3d centerp = p - center; + const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), centerp.normalized()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * q * + Geometry::scale_transform({ coplanar ? centerp.norm() : radius, 1.0f, 1.0f })); + m_dimensioning.line.render(); + }; + render_extension(on_e1_side ? e.first : e.second); + } + }; + + auto render_arc = [this, shader, ¢er]() { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); + }; + + render_edge_entension({ e11_proj , e12_proj }, true); + render_edge_entension({ e21_proj , e22_proj }, coplanar); + render_arc(); + }; + + auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { + // distance const auto [distance, v1, v2] = distance_edge_edge(e1, e2); point_point(v1, v2); + // arc + arc_edge_edge(e1, e2); }; auto edge_circle = [point_point](const Edge& e, const Circle& c) { @@ -934,6 +1058,9 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.init_from(std::move(init_data)); } + if (last_selected_features != m_selected_features) + m_dimensioning.arc.reset(); + glsafe(::glDisable(GL_DEPTH_TEST)); // point-point diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 4417a0d608..51094ae12c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -80,7 +80,8 @@ class GLGizmoMeasure : public GLGizmoBase { GLModel line; GLModel triangle; - }; + GLModel arc; + }; Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; From 706d05b31fef20a68eb6ea9cbb333d440d16389d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 23 Sep 2022 12:44:06 +0200 Subject: [PATCH 249/327] Measuring: Gizmo measure shows dimensioning for angle edge-plane --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 284 +++++++++++++++-------- 1 file changed, 181 insertions(+), 103 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f6a6a46ca5..fc49e9883f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -75,20 +75,33 @@ static std::tuple distance_point_plane(const Vec3d& v, con return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); } +static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) +{ + return (to - from).normalized(); +} + +static Vec3d edge_direction(const Edge& e) +{ + return vector_direction(e.first, e.second); +} + +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) { - const Eigen::ParametrizedLine line(e.first, (e.second - e.first).normalized()); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); return std::make_tuple(line.distance(v), v, line.projection(v)); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) { const auto& [center, radius, normal] = c; const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * (plane.projection(v) - center).normalized(); + const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; @@ -115,18 +128,19 @@ static std::tuple distance_edge_edge(const Edge& e1, const return distances.front(); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_circle(const Edge& e, const Circle& c) { const auto& [center, radius, normal] = c; const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = e1e2.normalized(); + const Vec3d e1e2_unit = vector_direction(e.first, e.second); std::vector> distances; distances.emplace_back(distance_point_circle(e.first, c)); distances.emplace_back(distance_point_circle(e.second, c)); const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line(e.first, e1e2_unit); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); const Vec3d inter = line.intersectionPoint(plane); const Vec3d e1inter = inter - e.first; if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) @@ -139,6 +153,7 @@ static std::tuple distance_edge_circle(const Edge& e, cons return distances.front(); } +// returns: distance, 1st vertex, 2nd vertex static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) { const auto& [idx1, normal1, origin1] = p1; @@ -147,6 +162,71 @@ static std::tuple distance_plane_plane(const Plane& p1, co std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); } +// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar +// After return, the edges are oriented so that they point away from their intersection point +static std::tuple angle_edge_edge(Edge& e1, Edge& e2) +{ + Vec3d e1_unit = edge_direction(e1); + Vec3d e2_unit = edge_direction(e2); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return std::make_tuple(0.0, e1.first, 0.0, true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e1.first, e1.second); + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e2.first, e2.second); + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return std::make_tuple(angle, center, radius, coplanar); +} + static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -884,73 +964,21 @@ void GLGizmoMeasure::render_dimensioning() point_point(v1, v2); }; - auto arc_edge_edge = [this, shader](Edge e1, Edge e2) { - Vec3d e1_unit = (e1.second - e1.first).normalized(); - Vec3d e2_unit = (e2.second - e2.first).normalized(); - const double dot = e1_unit.dot(e2_unit); - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - // edges are parallel, return + auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { + Edge e1copy = e1; + Edge e2copy = e2; + const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + + if (radius == 0.0) return; - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); + assert(force_radius == nullptr || *force_radius > 0.0); - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // center in world coordinate - const Vec3d center = qp.inverse() * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // revert edges, if needed (we want them to move away from the center) - unsigned int revert_count = 0; - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - ++revert_count; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - ++revert_count; - } - - if (revert_count == 1) { - normal = -normal; - qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - qp_inverse = qp.inverse(); - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + const Vec3d e1_unit = edge_direction(e1copy); + const Vec3d e2_unit = edge_direction(e2copy); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); @@ -964,7 +992,7 @@ void GLGizmoMeasure::render_dimensioning() const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); - const Vec3d v = radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); + const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); init_data.add_vertex((Vec3f)v.cast()); init_data.add_index(i); } @@ -972,35 +1000,70 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.init_from(std::move(init_data)); } - auto render_edge_entension = [this, shader, ¢er, radius](const Edge& e, bool coplanar) { - const Vec3d e1center = center - e.first; - const Vec3d e1e2 = e.second - e.first; - const bool on_e1_side = e1center.dot(e1e2) < -EPSILON; - const bool on_e2_side = !on_e1_side && e1center.norm() > e1e2.norm(); - if (!coplanar || on_e1_side || on_e2_side) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - auto render_extension = [this, shader, &camera, ¢er, radius, coplanar](const Vec3d& p) { - const Vec3d centerp = p - center; - const auto q = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), centerp.normalized()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * q * - Geometry::scale_transform({ coplanar ? centerp.norm() : radius, 1.0f, 1.0f })); - m_dimensioning.line.render(); - }; - render_extension(on_e1_side ? e.first : e.second); - } - }; + // render arc + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + m_dimensioning.arc.render(); - auto render_arc = [this, shader, ¢er]() { + // render edge 1 extension + const Vec3d e11e12 = e1copy.second - e1copy.first; + const Vec3d e11center = center - e1copy.first; + const double e11center_len = e11center.norm(); + if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); - m_dimensioning.arc.render(); - }; + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } - render_edge_entension({ e11_proj , e12_proj }, true); - render_edge_entension({ e21_proj , e22_proj }, coplanar); - render_arc(); + // render edge 2 extension + const Vec3d e21center = center - e2copy.first; + const double e21center_len = e21center.norm(); + if (e21center_len > EPSILON) { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); + m_dimensioning.line.render(); + } + }; + + auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + const auto& [idx, normal, origin] = p; + const Vec3d e1e2 = e.second - e.first; + const double abs_dot = std::abs(normal.dot(edge_direction(e))); + if (abs_dot < EPSILON || std::abs(abs_dot - 1.0) < EPSILON) + return; + + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // ensure the edge is pointing away from the intersection + Edge ecopy = e; + Vec3d e1e2copy = e1e2; + if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { + std::swap(ecopy.first, ecopy.second); + e1e2copy = -e1e2copy; + } + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2copy); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2copy.norm() * edge_on_plane_unit }; + + const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); + const double radius = (inters - e1e2copy_mid).norm(); + arc_edge_edge(ecopy, edge_on_plane, &radius); }; auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { @@ -1011,6 +1074,11 @@ void GLGizmoMeasure::render_dimensioning() arc_edge_edge(e1, e2); }; + auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { + // arc + arc_edge_plane(e, p); + }; + auto edge_circle = [point_point](const Edge& e, const Circle& c) { const auto [distance, v1, v2] = distance_edge_circle(e, c); point_point(v1, v2); @@ -1088,31 +1156,41 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } // edge-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); } + // edge-plane + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { + edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); + } // edge-circle else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); } + // plane-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); + } + // plane-edge + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { + edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); + } // plane-plane else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } + // circle-point + else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && + m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); + } // circle-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { From 10459706b8d1149f39b38c43b1a2d625b03ed10d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 26 Sep 2022 13:17:41 +0200 Subject: [PATCH 250/327] Fixed bug in get_measurement() function --- src/libslic3r/Measure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 1a79369a3e..c2b7edd5e2 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -518,7 +518,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto& [idx2, normal2, pt2] = f2.get_plane(); double angle = 0.; - if (! normal1.isApprox(normal2)) { + if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); result.distance_infinite = plane.absDistance(pt2); From aee76f0c11f8965574425b3e58f66024ee0bba9d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 27 Sep 2022 10:21:57 +0200 Subject: [PATCH 251/327] Measuring - Refactoring in GLGizmoMeasure related to scene raycasters state cache Fixed conflicts while rebasing to master --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 25 +++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 9 +++++++-- src/slic3r/GUI/SceneRaycaster.cpp | 25 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index fc49e9883f..c511f7e501 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -372,11 +372,13 @@ void GLGizmoMeasure::on_set_state() else { m_mode = EMode::BasicSelection; // store current state of scene raycaster for later use - m_scene_raycaster_state.clear(); - m_scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - if (m_scene_raycasters != nullptr) { - for (const auto& r : *m_scene_raycasters) { - m_scene_raycaster_state.emplace_back(r->is_active()); + m_scene_raycasters.clear(); + auto scene_raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); + if (scene_raycasters != nullptr) { + m_scene_raycasters.reserve(scene_raycasters->size()); + for (auto r : *scene_raycasters) { + SceneRaycasterState state = { r, r->is_active() }; + m_scene_raycasters.emplace_back(state); } } } @@ -772,20 +774,15 @@ void GLGizmoMeasure::update_if_needed() void GLGizmoMeasure::disable_scene_raycasters() { - if (m_scene_raycasters != nullptr) { - for (auto r : *m_scene_raycasters) { - r->set_active(false); - } + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(false); } } void GLGizmoMeasure::restore_scene_raycasters_state() { - if (m_scene_raycasters != nullptr) { - assert(m_scene_raycasters->size() == m_scene_raycaster_state.size()); - for (size_t i = 0; i < m_scene_raycasters->size(); ++i) { - (*m_scene_raycasters)[i]->set_active(m_scene_raycaster_state[i]); - } + for (auto r : m_scene_raycasters) { + r.raycaster->set_active(r.state); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 51094ae12c..08a9042b2c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -89,8 +89,13 @@ class GLGizmoMeasure : public GLGizmoBase std::map> m_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; - std::vector>* m_scene_raycasters{ nullptr }; - std::vector m_scene_raycaster_state; + struct SceneRaycasterState + { + std::shared_ptr raycaster{ nullptr }; + bool state{true}; + + }; + std::vector m_scene_raycasters; // These hold information to decide whether recalculation is necessary: std::vector m_volumes_matrices; diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index 6d255ccdb4..a3e95293b9 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -173,6 +173,31 @@ void SceneRaycaster::render_hit(const Camera& camera) shader->stop_using(); } + +size_t SceneRaycaster::active_beds_count() const { + size_t count = 0; + for (const auto& b : m_bed) { + if (b->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_volumes_count() const { + size_t count = 0; + for (const auto& v : m_volumes) { + if (v->is_active()) + ++count; + } + return count; +} +size_t SceneRaycaster::active_gizmos_count() const { + size_t count = 0; + for (const auto& g : m_gizmos) { + if (g->is_active()) + ++count; + } + return count; +} #endif // ENABLE_RAYCAST_PICKING_DEBUG std::vector>* SceneRaycaster::get_raycasters(EType type) From e0bfb17e64bc69957318d96c7f209d56c855fdbc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 29 Sep 2022 08:43:03 +0200 Subject: [PATCH 252/327] Measuring - GLGizmoMeasure - Allow to deselect second feature by clicking on it Fixed conflicts while rebasing to master --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 83 +++++++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + src/slic3r/GUI/SceneRaycaster.cpp | 6 +- src/slic3r/GUI/SceneRaycaster.hpp | 3 +- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c511f7e501..5dee42ad1e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -30,6 +30,8 @@ static const int POINT_ID = 100; static const int EDGE_ID = 200; static const int CIRCLE_ID = 300; static const int PLANE_ID = 400; +static const int SELECTION_1_ID = 501; +static const int SELECTION_2_ID = 502; static const float TRIANGLE_BASE = 10.0f; static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f; @@ -47,11 +49,11 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty switch (type) { default: - case Measure::SurfaceFeatureType::Undef: { return L("Undefined"); } - case Measure::SurfaceFeatureType::Point: { return L("Vertex"); } - case Measure::SurfaceFeatureType::Edge: { return L("Edge"); } - case Measure::SurfaceFeatureType::Circle: { return L("Circle"); } - case Measure::SurfaceFeatureType::Plane: { return L("Plane"); } + case Measure::SurfaceFeatureType::Undef: { return _u8L("Undefined"); } + case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } + case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } + case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } + case Measure::SurfaceFeatureType::Plane: { return _u8L("Plane"); } } } @@ -277,20 +279,44 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; auto item_from_feature = [this]() { - const SelectedFeatures::Item item = { - (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), - (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature }; + SelectedFeatures::Item item; + if (m_hover_id == SELECTION_1_ID && m_selected_features.first.feature.has_value()) + item = m_selected_features.first; + else if (m_hover_id == SELECTION_2_ID && m_selected_features.second.feature.has_value()) + item = m_selected_features.second; + else { + item = { + (m_mode == EMode::ExtendedSelection) ? point_on_feature_type_as_string(m_curr_feature->get_type(), m_hover_id) : surface_feature_type_as_string(m_curr_feature->get_type()), + (m_mode == EMode::ExtendedSelection) ? Measure::SurfaceFeature(*m_curr_point_on_feature_position) : m_curr_feature + }; + } return item; }; if (m_selected_features.first.feature.has_value()) { - const SelectedFeatures::Item item = item_from_feature(); - if (m_selected_features.first != item) - m_selected_features.second = item; - } - else - m_selected_features.first = item_from_feature(); + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); + if (it != m_selection_raycasters.end()) + m_selection_raycasters.erase(it); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID); + const SelectedFeatures::Item item = item_from_feature(); + if (m_selected_features.first != item) { + if (m_selected_features.second == item) + m_selected_features.second.reset(); + else { + m_selected_features.second = item; + if (m_mode == EMode::ExtendedSelection) + m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster)); + } + } + } + else { + const SelectedFeatures::Item item = item_from_feature(); + m_selected_features.first = item; + if (m_mode == EMode::ExtendedSelection) + m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); + } return true; } @@ -308,6 +334,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.RightDown() && mouse_event.CmdDown()) { m_selected_features.reset(); + m_selection_raycasters.clear(); m_imgui->set_requires_extra_frame(); } else if (mouse_event.Leaving()) @@ -332,6 +359,7 @@ void GLGizmoMeasure::data_changed() m_last_inv_zoom = 0.0f; m_last_plane_idx = -1; m_selected_features.reset(); + m_selection_raycasters.clear(); } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -435,7 +463,11 @@ void GLGizmoMeasure::on_render() std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); if (m_curr_feature != curr_feature) { - GLGizmoMeasure::on_unregister_raycasters_for_picking(); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + m_raycasters.clear(); m_curr_feature = curr_feature; if (!m_curr_feature.has_value()) return; @@ -687,11 +719,23 @@ void GLGizmoMeasure::on_render() std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); + if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); + if (it != m_selection_raycasters.end()) + (*it)->set_transform(m_volume_matrix * Geometry::translation_transform(m_selected_features.first.feature->get_point()) * Geometry::scale_transform(inv_zoom)); + } } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); + if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { + auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), + [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); + if (it != m_selection_raycasters.end()) + (*it)->set_transform(m_volume_matrix * Geometry::translation_transform(m_selected_features.second.feature->get_point()) * Geometry::scale_transform(inv_zoom)); + } } if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { @@ -1269,7 +1313,7 @@ void GLGizmoMeasure::render_debug_dialog() add_strings_row_to_table(*m_imgui, "m_pt3", ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(*extra_point), ImGui::GetStyleColorVec4(ImGuiCol_Text)); }; - m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(_L("Measure tool debug"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (!m_selected_features.first.feature.has_value() && !m_selected_features.second.feature.has_value()) m_imgui->text("Empty selection"); else { @@ -1323,7 +1367,10 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); }, [this]() { - m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point")); + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), + m_selected_features.second.feature.has_value() ? + ((m_mode == EMode::BasicSelection) ? _u8L("Select/Unselect feature") : _u8L("Select/Unselect point")) : + ((m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"))); ImGui::SameLine(); const ImVec2 pos = ImGui::GetCursorScreenPos(); const float rect_size = ImGui::GetTextLineHeight(); @@ -1434,6 +1481,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit //if (m_selected_features.first.feature.has_value()) { // if (m_imgui->button(_u8L("Restart"))) { // m_selected_features.reset(); + // m_selection_raycasters.clear(); // m_imgui->set_requires_extra_frame(); // } //} @@ -1498,6 +1546,7 @@ void GLGizmoMeasure::on_unregister_raycasters_for_picking() m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo); m_parent.set_raycaster_gizmos_on_top(false); m_raycasters.clear(); + m_selection_raycasters.clear(); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 08a9042b2c..add13eb4c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,6 +87,7 @@ class GLGizmoMeasure : public GLGizmoBase Transform3d m_volume_matrix{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; + std::vector> m_selection_raycasters; std::optional m_curr_feature; std::optional m_curr_point_on_feature_position; struct SceneRaycasterState diff --git a/src/slic3r/GUI/SceneRaycaster.cpp b/src/slic3r/GUI/SceneRaycaster.cpp index a3e95293b9..96ae536ca7 100644 --- a/src/slic3r/GUI/SceneRaycaster.cpp +++ b/src/slic3r/GUI/SceneRaycaster.cpp @@ -37,10 +37,10 @@ std::shared_ptr SceneRaycaster::add_raycaster(EType type, in const Transform3d& trafo, bool use_back_faces) { switch (type) { - case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + case EType::Bed: { return m_bed.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } case EType::Volume: { return m_volumes.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } - case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } - default: { assert(false); return nullptr; } + case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared(encode_id(type, id), raycaster, trafo, use_back_faces)); } + default: { assert(false); return nullptr; } }; } diff --git a/src/slic3r/GUI/SceneRaycaster.hpp b/src/slic3r/GUI/SceneRaycaster.hpp index 7541badbb4..2254a2022d 100644 --- a/src/slic3r/GUI/SceneRaycaster.hpp +++ b/src/slic3r/GUI/SceneRaycaster.hpp @@ -105,9 +105,10 @@ public: size_t active_gizmos_count() const; #endif // ENABLE_RAYCAST_PICKING_DEBUG + static int decode_id(EType type, int id); + private: static int encode_id(EType type, int id); - static int decode_id(EType type, int id); static int base_id(EType type); }; From 1f9d42b14ff7d8a320971c45ffec2b6fc3ab36ba Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 29 Sep 2022 10:19:40 +0200 Subject: [PATCH 253/327] Measuring - GLGizmoMeasure - Added option to copy to clipboard the result of measurement Fixed conflicts while rebasing to master --- src/imgui/imconfig.h | 16 +++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 57 ++++++++++++++++++++---- src/slic3r/GUI/ImGuiWrapper.cpp | 3 ++ 3 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index f2c3ef0837..7c79058c4c 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -156,6 +156,21 @@ namespace ImGui const wchar_t InfoMarker = 0x2603; const wchar_t SliderFloatEditBtnIcon = 0x2604; const wchar_t SliderFloatEditBtnPressedIcon = 0x2605; +#if ENABLE_MEASURE_GIZMO + const wchar_t ClipboardBtnIcon = 0x2606; + const wchar_t LegendTravel = 0x2701; + const wchar_t LegendWipe = 0x2702; + const wchar_t LegendRetract = 0x2703; + const wchar_t LegendDeretract = 0x2704; + const wchar_t LegendSeams = 0x2705; + const wchar_t LegendToolChanges = 0x2706; + const wchar_t LegendColorChanges = 0x2707; + const wchar_t LegendPausePrints = 0x2708; + const wchar_t LegendCustomGCodes = 0x2709; + const wchar_t LegendCOG = 0x2710; + const wchar_t LegendShells = 0x2711; + const wchar_t LegendToolMarker = 0x2712; +#else const wchar_t LegendTravel = 0x2606; const wchar_t LegendWipe = 0x2607; const wchar_t LegendRetract = 0x2608; @@ -168,6 +183,7 @@ namespace ImGui const wchar_t LegendCOG = 0x2615; const wchar_t LegendShells = 0x2616; const wchar_t LegendToolMarker = 0x2617; +#endif // ENABLE_MEASURE_GIZMO // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5dee42ad1e..c232405019 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -10,6 +10,8 @@ #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" +#include + #include #include @@ -1251,7 +1253,7 @@ static void add_row_to_table(std::function col_1 = nullptr, std::fun col_1(); ImGui::TableSetColumnIndex(1); col_2(); -}; +} static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { @@ -1263,14 +1265,14 @@ static std::string format_double(double value) char buf[1024]; sprintf(buf, "%.3f", value); return std::string(buf); -}; +} static std::string format_vec3(const Vec3d& v) { char buf[1024]; sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); return std::string(buf); -}; +} #if ENABLE_MEASURE_GIZMO_DEBUG void GLGizmoMeasure::render_debug_dialog() @@ -1486,36 +1488,75 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit // } //} + auto add_measure_row_to_table = [this](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + m_imgui->text_colored(col_1_color, col_1); + ImGui::TableSetColumnIndex(1); + m_imgui->text_colored(col_2_color, col_2); + ImGui::TableSetColumnIndex(2); + ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + 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.25f, 0.25f, 0.25f, 1.0f }); + if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + wxTheClipboard->Open(); + wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); + wxTheClipboard->Close(); + } + ImGui::PopStyleColor(3); + if (ImGui::IsItemHovered()) { + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); + } + }; + if (m_selected_features.second.feature.has_value()) { const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); - if (ImGui::BeginTable("Measure", 2)) { + if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - add_strings_row_to_table(*m_imgui, _u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + ImGui::PushID((void*)(intptr_t)1); + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_infinite.has_value()) { double distance = *measure.distance_infinite; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + ImGui::PushID((void*)(intptr_t)2); + add_measure_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_strict.has_value()) { double distance = *measure.distance_strict; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + ImGui::PushID((void*)(intptr_t)3); + add_measure_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } if (measure.distance_xyz.has_value()) { Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - add_strings_row_to_table(*m_imgui, _u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + ImGui::PushID((void*)(intptr_t)4); + add_measure_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ImGui::PopID(); } ImGui::EndTable(); } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index fe3f69a58b..b8c6c463f5 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -56,6 +56,9 @@ static const std::map font_icons = { {ImGui::PreferencesHoverButton, "notification_preferences_hover"}, {ImGui::SliderFloatEditBtnIcon, "edit_button" }, {ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" }, +#if ENABLE_MEASURE_GIZMO + {ImGui::ClipboardBtnIcon , "copy_menu" }, +#endif // ENABLE_MEASURE_GIZMO }; static const std::map font_icons_large = { From 9658c8c6774ec2d75dd097d9ebcfad44c96820cb Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 11:22:23 +0200 Subject: [PATCH 254/327] Measurement: moving arrow-drawing functions from frontend to the backend (1/4) --- src/libslic3r/Measure.cpp | 24 +++-- src/libslic3r/Measure.hpp | 10 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 126 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 +- 4 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index c2b7edd5e2..a627fe62cc 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -349,7 +349,7 @@ std::optional MeasuringImpl::get_feature(size_t face_idx, const // which is needless and relatively expensive. res = get_measurement(plane.surface_features[i], point_sf); if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented. - double dist = *res.distance_strict; + double dist = res.distance_strict->dist; if (dist < feature_hover_limit && dist < min_dist) { min_dist = std::min(dist, min_dist); closest_feature_idx = i; @@ -455,8 +455,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f1.get_type() == SurfaceFeatureType::Point) { if (f2.get_type() == SurfaceFeatureType::Point) { Vec3d diff = (f2.get_point() - f1.get_point()); - result.distance_strict = diff.norm(); + result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()}); result.distance_xyz = diff; + /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { const auto& [s,e] = f2.get_edge(); @@ -468,11 +469,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(dist_inf); + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO } else { // the result is the closer of the endpoints - result.distance_strict = std::make_optional(std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf)); + bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(dist_inf); + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -482,12 +484,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm()); - result.distance_strict = std::make_optional(dist); + const Vec3d p_on_circle = c + radius * (circle_plane.projection(f1.get_point()) - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { const auto& [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); - result.distance_infinite = plane.absDistance(f1.get_point()); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = } /////////////////////////////////////////////////////////////////////////// @@ -495,18 +498,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// @@ -521,7 +529,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (normal1.isApprox(normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = plane.absDistance(pt2); + result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); } else { // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 5d71983f0b..6b7940f2bf 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -110,12 +110,16 @@ private: }; - +struct DistAndPoints { + double dist; + Vec3d from; + Vec3d to; +}; struct MeasurementResult { std::optional angle; - std::optional distance_infinite; - std::optional distance_strict; + std::optional distance_infinite; + std::optional distance_strict; std::optional distance_xyz; bool has_any_data() const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index c232405019..11cd651e5c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -72,13 +72,6 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static std::tuple distance_point_plane(const Vec3d& v, const Plane& p) -{ - const auto& [idx, normal, origin] = p; - const Eigen::Hyperplane plane(normal, origin); - return std::make_tuple(plane.absDistance(v), v, plane.projection(v)); -} - static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); @@ -89,21 +82,13 @@ static Vec3d edge_direction(const Edge& e) return vector_direction(e.first, e.second); } -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_edge(const Vec3d& v, const Edge& e) -{ - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - return std::make_tuple(line.distance(v), v, line.projection(v)); -} -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_point_circle(const Vec3d& v, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Eigen::Hyperplane plane(normal, center); - const Vec3d p_on_circle = center + radius * vector_direction(center, plane.projection(v)); - return std::make_tuple((v - p_on_circle).norm(), v, p_on_circle); -} + + + + +/* + // returns: distance, 1st vertex, 2nd vertex static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) @@ -165,6 +150,34 @@ static std::tuple distance_plane_plane(const Plane& p1, co return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); } +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + // returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar // After return, the edges are oriented so that they point away from their intersection point @@ -278,6 +291,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { + SelectedFeatures selected_features_old = m_selected_features; + + m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -319,6 +335,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) if (m_mode == EMode::ExtendedSelection) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); } + + if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + return true; } @@ -963,8 +983,11 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; + auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v_proj] = distance_point_edge(v, e); + const double distance = m_measurement_result.distance_infinite->dist; + const Vec3d v1 = m_measurement_result.distance_infinite->from; + const Vec3d v_proj = m_measurement_result.distance_infinite->to; point_point(v1, v_proj); const Vec3d e1e2 = e.second - e.first; @@ -997,16 +1020,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto point_plane = [point_point](const Vec3d& v, const Plane& p) { - const auto [distance, v1, v2] = distance_point_plane(v, p); - point_point(v1, v2); - }; - - auto point_circle = [point_point](const Vec3d& v, const Circle& c) { - const auto [distance, v1, v2] = distance_point_circle(v, c); - point_point(v1, v2); - }; - +/* auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { Edge e1copy = e1; Edge e2copy = e2; @@ -1130,7 +1144,7 @@ void GLGizmoMeasure::render_dimensioning() auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { const auto [distance, v1, v2] = distance_plane_plane(p1, p2); point_point(v1, v2); - }; + };*/ shader->start_using(); @@ -1174,26 +1188,26 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // point-point - if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_point(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_point()); + // Render the arrow between the points that the backend passed: + if (m_selected_features.second.feature.has_value()) { + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to); } + + // Now if one of the features is an edge, draw also the extension of the edge to where the dist is measured: + // TODO... + + // point-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); } - // point-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - point_plane(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_plane()); - } - // point-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - point_circle(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_circle()); - } + + + /* // edge-point else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { @@ -1214,11 +1228,6 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); } - // plane-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_plane(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_plane()); - } // plane-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { @@ -1229,16 +1238,12 @@ void GLGizmoMeasure::render_dimensioning() m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); } - // circle-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_circle(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_circle()); - } // circle-edge else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); } +*/ glsafe(::glEnable(GL_DEPTH_TEST)); @@ -1520,7 +1525,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit }; if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult measure = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); if (measure.has_any_data()) { m_imgui->text(_u8L("Measure") + ":"); @@ -1532,7 +1538,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_infinite.has_value()) { - double distance = *measure.distance_infinite; + double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); @@ -1541,7 +1547,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::PopID(); } if (measure.distance_strict.has_value()) { - double distance = *measure.distance_strict; + double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index add13eb4c3..7e65b8c89c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -70,7 +70,9 @@ class GLGizmoMeasure : public GLGizmoBase }; EMode m_mode{ EMode::BasicSelection }; - std::unique_ptr m_measuring; + Measure::MeasurementResult m_measurement_result; + + std::unique_ptr m_measuring; // PIMPL PickingModel m_sphere; PickingModel m_cylinder; From 8af3e5823a33872a60aa36c0aa3faeb1cc377453 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 12:33:20 +0200 Subject: [PATCH 255/327] Measurement: moving arrow-drawing functions from frontend to the backend (2/4) --- src/libslic3r/Measure.cpp | 35 +++++++++++++++++++++++- src/libslic3r/Measure.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index a627fe62cc..01f48579d2 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -498,7 +498,40 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + + std::vector distances; + + auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { + //const auto [distance, v1, v2] = distance_point_edge(v, SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); + + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + double distance = res.distance_strict->dist; + Vec3d v1 = res.distance_strict->from; + Vec3d v2 = res.distance_strict->to; + + + const Vec3d e1e2 = e.second - e.first; + const Vec3d e1v2 = v2 - e.first; + if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) + distances.emplace_back(distance, v, v2); + }; + + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); + + distances.emplace_back((e2.first - e1.first).norm(), e1.first, e2.first); + distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); + distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); + distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); + add_point_edge_distance(e1.first, e2); + add_point_edge_distance(e1.second, e2); + add_point_edge_distance(e2.first, e1); + add_point_edge_distance(e2.second, e1); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(*it); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 6b7940f2bf..fb36b50095 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -111,6 +111,7 @@ private: struct DistAndPoints { + DistAndPoints(double dist_, Vec3d from_, Vec3d to_) : dist(dist_), from(from_), to(to_) {} double dist; Vec3d from; Vec3d to; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 11cd651e5c..222d8087a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -90,7 +90,7 @@ static Vec3d edge_direction(const Edge& e) /* -// returns: distance, 1st vertex, 2nd vertex +// returns: distance, 1st vertex, 2nd vertexs static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) { std::vector> distances; From 79ab1ab1b50ab3dd1bbbcc5854cb010688f4027a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 14:50:29 +0200 Subject: [PATCH 256/327] Measurement: moving arrow-drawing functions from frontend to the backend (3/4) --- src/libslic3r/Measure.cpp | 98 ++++++- src/libslic3r/Measure.hpp | 25 +- src/libslic3r/SurfaceMesh.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 310 ++++------------------- 4 files changed, 156 insertions(+), 278 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 01f48579d2..fbf248c0f7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -431,6 +431,67 @@ std::vector> Measuring::get_planes_triangle_indices() const +static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +{ + Vec3d e1_unit = (e1.second - e1.first).normalized(); + Vec3d e2_unit = (e2.second - e2.first).normalized(); + const double dot = e1_unit.dot(e2_unit); + // are edges parallel ? + if (std::abs(std::abs(dot) - 1.0) < EPSILON) + return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + + // project edges on the plane defined by them + Vec3d normal = e1_unit.cross(e2_unit).normalized(); + const Eigen::Hyperplane plane(normal, e1.first); + Vec3d e11_proj = plane.projection(e1.first); + Vec3d e12_proj = plane.projection(e1.second); + Vec3d e21_proj = plane.projection(e2.first); + Vec3d e22_proj = plane.projection(e2.second); + + const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; + + // rotate the plane to become the XY plane + auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); + auto qp_inverse = qp.inverse(); + const Vec3d e11_rot = qp * e11_proj; + const Vec3d e12_rot = qp * e12_proj; + const Vec3d e21_rot = qp * e21_proj; + const Vec3d e22_rot = qp * e22_proj; + + // discard Z + const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); + const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); + const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); + const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); + + // find intersection (arc center) of edges in XY plane + const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); + const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); + const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); + + // arc center in original coordinate + const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); + + // ensure the edges are pointing away from the center + if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { + std::swap(e11_proj, e12_proj); + e1_unit = -e1_unit; + } + if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { + std::swap(e21_proj, e22_proj); + e2_unit = -e2_unit; + } + + // arc angle + const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); + // arc radius + const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); + const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); + const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); + + return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); +} + @@ -469,12 +530,12 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite - result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO + result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints bool s_is_closer = dist_start_sq < dist_end_sq; result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); } - result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); // TODO + result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. @@ -498,18 +559,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Edge) { if (f2.get_type() == SurfaceFeatureType::Edge) { - std::vector distances; auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - //const auto [distance, v1, v2] = distance_point_edge(v, SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); double distance = res.distance_strict->dist; - Vec3d v1 = res.distance_strict->from; Vec3d v2 = res.distance_strict->to; - const Vec3d e1e2 = e.second - e.first; const Vec3d e1v2 = v2 - e.first; if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) @@ -531,10 +587,32 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& [](const DistAndPoints& item1, const DistAndPoints& item2) { return item1.dist < item2.dist; }); - result.distance_infinite = std::make_optional(*it); // TODO + result.distance_infinite = std::make_optional(*it); + + result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge()); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + const std::pair e = f1.get_edge(); + const auto& [center, radius, normal] = f2.get_circle(); + const Vec3d e1e2 = (e.second - e.first); + const Vec3d e1e2_unit = (e.second - e.first).normalized(); + + std::vector distances; + distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); + distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict); + + const Eigen::Hyperplane plane(e1e2_unit, center); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inter = line.intersectionPoint(plane); + const Vec3d e1inter = inter - e.first; + if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) + distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict); + + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO @@ -567,7 +645,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = angle; + result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index fb36b50095..b88411a9f6 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -15,12 +15,12 @@ namespace Slic3r { namespace Measure { -enum class SurfaceFeatureType { - Undef, - Point, - Edge, - Circle, - Plane +enum class SurfaceFeatureType : int { + Undef = 0, + Point = 1 << 0, + Edge = 1 << 1, + Circle = 1 << 2, + Plane = 1 << 3 }; class SurfaceFeature { @@ -117,8 +117,19 @@ struct DistAndPoints { Vec3d to; }; +struct AngleAndPoints { + AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} + double angle; + Vec3d center; + Vec3d e1; + Vec3d e2; + double radius; + bool coplanar; +}; + struct MeasurementResult { - std::optional angle; + std::optional angle; std::optional distance_infinite; std::optional distance_strict; std::optional distance_xyz; diff --git a/src/libslic3r/SurfaceMesh.hpp b/src/libslic3r/SurfaceMesh.hpp index a4b261ceb9..9e547eec49 100644 --- a/src/libslic3r/SurfaceMesh.hpp +++ b/src/libslic3r/SurfaceMesh.hpp @@ -2,6 +2,7 @@ #define slic3r_SurfaceMesh_hpp_ #include +#include namespace Slic3r { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 222d8087a0..e9fbd7f8c0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -16,15 +16,13 @@ #include +#include + #if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace GUI { -using Edge = std::pair; -using Plane = std::tuple; -using Circle = std::tuple; - static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = { 0.25f, 0.75f, 0.75f, 1.0f }; static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = { 0.75f, 0.25f, 0.75f, 1.0f }; @@ -72,176 +70,9 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static Vec3d vector_direction(const Vec3d& from, const Vec3d& to) +static Vec3d edge_direction(const std::pair& e) { - return (to - from).normalized(); -} - -static Vec3d edge_direction(const Edge& e) -{ - return vector_direction(e.first, e.second); -} - - - - - - -/* - - -// returns: distance, 1st vertex, 2nd vertexs -static std::tuple distance_edge_edge(const Edge& e1, const Edge& e2) -{ - std::vector> distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const Edge& e) { - const auto [distance, v1, v2] = distance_point_edge(v, e); - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(std::make_tuple(distance, v, v2)); - }; - - distances.emplace_back(std::make_tuple((e2.first - e1.first).norm(), e1.first, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.first).norm(), e1.first, e2.second)); - distances.emplace_back(std::make_tuple((e2.first - e1.second).norm(), e1.second, e2.first)); - distances.emplace_back(std::make_tuple((e2.second - e1.second).norm(), e1.second, e2.second)); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_edge_circle(const Edge& e, const Circle& c) -{ - const auto& [center, radius, normal] = c; - const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = vector_direction(e.first, e.second); - - std::vector> distances; - distances.emplace_back(distance_point_circle(e.first, c)); - distances.emplace_back(distance_point_circle(e.second, c)); - - const Eigen::Hyperplane plane(e1e2_unit, center); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inter = line.intersectionPoint(plane); - const Vec3d e1inter = inter - e.first; - if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) - distances.emplace_back(distance_point_circle(inter, c)); - - std::sort(distances.begin(), distances.end(), - [](const std::tuple& item1, const std::tuple& item2) { - return std::get<0>(item1) < std::get<0>(item2); - }); - return distances.front(); -} - -// returns: distance, 1st vertex, 2nd vertex -static std::tuple distance_plane_plane(const Plane& p1, const Plane& p2) -{ - const auto& [idx1, normal1, origin1] = p1; - const auto& [idx2, normal2, origin2] = p2; - return (std::abs(std::abs(normal1.dot(normal2)) - 1.0) < EPSILON) ? distance_point_plane(origin2, p1) : - std::make_tuple(0.0, Vec3d::Zero(), Vec3d::Zero()); -} -*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// returns: angle in rad, center of arc, radius of arc, whether or not the edges are coplanar -// After return, the edges are oriented so that they point away from their intersection point -static std::tuple angle_edge_edge(Edge& e1, Edge& e2) -{ - Vec3d e1_unit = edge_direction(e1); - Vec3d e2_unit = edge_direction(e2); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) - return std::make_tuple(0.0, e1.first, 0.0, true); - - // project edges on the plane defined by them - Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const Eigen::Hyperplane plane(normal, e1.first); - Vec3d e11_proj = plane.projection(e1.first); - Vec3d e12_proj = plane.projection(e1.second); - Vec3d e21_proj = plane.projection(e2.first); - Vec3d e22_proj = plane.projection(e2.second); - - const bool coplanar = (e2.first - e21_proj).norm() < EPSILON && (e2.second - e22_proj).norm() < EPSILON; - - // rotate the plane to become the XY plane - auto qp = Eigen::Quaternion::FromTwoVectors(normal, Vec3d::UnitZ()); - auto qp_inverse = qp.inverse(); - const Vec3d e11_rot = qp * e11_proj; - const Vec3d e12_rot = qp * e12_proj; - const Vec3d e21_rot = qp * e21_proj; - const Vec3d e22_rot = qp * e22_proj; - - // discard Z - const Vec2d e11_rot_2d = Vec2d(e11_rot.x(), e11_rot.y()); - const Vec2d e12_rot_2d = Vec2d(e12_rot.x(), e12_rot.y()); - const Vec2d e21_rot_2d = Vec2d(e21_rot.x(), e21_rot.y()); - const Vec2d e22_rot_2d = Vec2d(e22_rot.x(), e22_rot.y()); - - // find intersection (arc center) of edges in XY plane - const Eigen::Hyperplane e1_rot_2d_line = Eigen::Hyperplane::Through(e11_rot_2d, e12_rot_2d); - const Eigen::Hyperplane e2_rot_2d_line = Eigen::Hyperplane::Through(e21_rot_2d, e22_rot_2d); - const Vec2d center_rot_2d = e1_rot_2d_line.intersection(e2_rot_2d_line); - - // arc center in original coordinate - const Vec3d center = qp_inverse * Vec3d(center_rot_2d.x(), center_rot_2d.y(), e11_rot.z()); - - // ensure the edges are pointing away from the center - if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { - std::swap(e1.first, e1.second); - std::swap(e11_proj, e12_proj); - e1_unit = -e1_unit; - } - if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { - std::swap(e2.first, e2.second); - std::swap(e21_proj, e22_proj); - e2_unit = -e2_unit; - } - - // arc angle - const double angle = std::acos(std::clamp(e1_unit.dot(e2_unit), -1.0, 1.0)); - // arc radius - const Vec3d e1_proj_mid = 0.5 * (e11_proj + e12_proj); - const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); - const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - - return std::make_tuple(angle, center, radius, coplanar); + return (e.second - e.first).normalized(); } static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) @@ -984,11 +815,9 @@ void GLGizmoMeasure::render_dimensioning() }; - auto point_edge = [this, shader, point_point](const Vec3d& v, const Edge& e) { - const double distance = m_measurement_result.distance_infinite->dist; - const Vec3d v1 = m_measurement_result.distance_infinite->from; + auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; - point_point(v1, v_proj); const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; @@ -1020,21 +849,30 @@ void GLGizmoMeasure::render_dimensioning() } }; -/* - auto arc_edge_edge = [this, shader](const Edge& e1, const Edge& e2, const double* const force_radius = nullptr) { - Edge e1copy = e1; - Edge e2copy = e2; - const auto [angle, center, radius, coplanar] = angle_edge_edge(e1copy, e2copy); + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double* const force_radius = nullptr) { + Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const double angle = res.angle->angle; + const Vec3d center = res.angle->center; + const Vec3d e1_unit = res.angle->e1; + const Vec3d e2_unit = res.angle->e2; + const double radius = res.angle->radius; + const bool coplanar = res.angle->coplanar; - if (radius == 0.0) + std::pair e1 = m_selected_features.first.feature->get_edge(); + std::pair e2 = m_selected_features.second.feature->get_edge(); + + if ((e1.second - e1.first).dot(e1_unit) < 0.) + std::swap(e1.first, e1.second); + if ((e2.second - e2.first).dot(e2_unit) < 0.) + std::swap(e2.first, e2.second); + + if (radius == 0.) return; assert(force_radius == nullptr || *force_radius > 0.0); - const double draw_radius = (force_radius != nullptr) ? *force_radius : radius; + double draw_radius = force_radius ? *force_radius : radius; - const Vec3d e1_unit = edge_direction(e1copy); - const Vec3d e2_unit = edge_direction(e2copy); const Vec3d normal = e1_unit.cross(e2_unit).normalized(); if (!m_dimensioning.arc.is_initialized()) { @@ -1064,31 +902,33 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.render(); // render edge 1 extension - const Vec3d e11e12 = e1copy.second - e1copy.first; - const Vec3d e11center = center - e1copy.first; + const Vec3d e11e12 = e1.second - e1.first; + const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } // render edge 2 extension - const Vec3d e21center = center - e2copy.first; + const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2copy)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; + +/* auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; @@ -1122,29 +962,7 @@ void GLGizmoMeasure::render_dimensioning() const double radius = (inters - e1e2copy_mid).norm(); arc_edge_edge(ecopy, edge_on_plane, &radius); }; - - auto edge_edge = [point_point, arc_edge_edge](const Edge& e1, const Edge& e2) { - // distance - const auto [distance, v1, v2] = distance_edge_edge(e1, e2); - point_point(v1, v2); - // arc - arc_edge_edge(e1, e2); - }; - - auto edge_plane = [point_point, arc_edge_plane](const Edge& e, const Plane& p) { - // arc - arc_edge_plane(e, p); - }; - - auto edge_circle = [point_point](const Edge& e, const Circle& c) { - const auto [distance, v1, v2] = distance_edge_circle(e, c); - point_point(v1, v2); - }; - - auto plane_plane = [point_point](const Plane& p1, const Plane& p2) { - const auto [distance, v1, v2] = distance_plane_plane(p1, p2); - point_point(v1, v2); - };*/ +*/ shader->start_using(); @@ -1188,63 +1006,33 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); - // Render the arrow between the points that the backend passed: if (m_selected_features.second.feature.has_value()) { + // Render the arrow between the points that the backend passed: const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; point_point(dap.from, dap.to); - } - // Now if one of the features is an edge, draw also the extension of the edge to where the dist is measured: - // TODO... + const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; + const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; + Measure::SurfaceFeatureType ft1 = f1.get_type(); + Measure::SurfaceFeatureType ft2 = f2.get_type(); - // point-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - point_edge(m_selected_features.first.feature->get_point(), m_selected_features.second.feature->get_edge()); + // Where needed, draw also the extension of the edge to where the dist is measured: + if ((int(ft1) | int(ft2)) == + (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) + point_edge(f1, f2); + + + // Now if there is an angle to show, draw the arc: + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) + arc_edge_edge(f1, f2); + //if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + // arc_edge_plane(); } - /* - // edge-point - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { - point_edge(m_selected_features.second.feature->get_point(), m_selected_features.first.feature->get_edge()); - } - // edge-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_edge(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_edge()); - } - // edge-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - edge_plane(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_plane()); - } - // edge-circle - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Edge && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Circle) { - edge_circle(m_selected_features.first.feature->get_edge(), m_selected_features.second.feature->get_circle()); - } - // plane-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_plane(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_plane()); - } - // plane-plane - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Plane && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Plane) { - plane_plane(m_selected_features.first.feature->get_plane(), m_selected_features.second.feature->get_plane()); - } - // circle-edge - else if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Circle && - m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Edge) { - edge_circle(m_selected_features.second.feature->get_edge(), m_selected_features.first.feature->get_circle()); - } -*/ - glsafe(::glEnable(GL_DEPTH_TEST)); shader->stop_using(); @@ -1533,7 +1321,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(*measure.angle)), + add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } From 7ceead76c880929b958dab3947bf997c94abf8ee Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 29 Sep 2022 16:39:39 +0200 Subject: [PATCH 257/327] Measurement: moving arrow-drawing functions from frontend to the backend (4/4) --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index e9fbd7f8c0..568d5db3a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -849,7 +849,7 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double* const force_radius = nullptr) { + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; @@ -928,8 +928,12 @@ void GLGizmoMeasure::render_dimensioning() }; -/* - auto arc_edge_plane = [this, arc_edge_edge](const Edge& e, const Plane& p) { + + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + + std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); + const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; const double abs_dot = std::abs(normal.dot(edge_direction(e))); @@ -941,7 +945,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d inters = line.intersectionPoint(plane); // ensure the edge is pointing away from the intersection - Edge ecopy = e; + std::pair ecopy = e; Vec3d e1e2copy = e1e2; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { std::swap(ecopy.first, ecopy.second); @@ -951,7 +955,7 @@ void GLGizmoMeasure::render_dimensioning() // calculate 2nd edge (on the plane) const Vec3d temp = normal.cross(e1e2copy); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - Edge edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); @@ -960,9 +964,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(ecopy, edge_on_plane, &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), + &radius); }; -*/ + shader->start_using(); @@ -1028,8 +1034,8 @@ void GLGizmoMeasure::render_dimensioning() // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) arc_edge_edge(f1, f2); - //if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) - // arc_edge_plane(); + if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) + arc_edge_plane(f1, f2); } From 3449ad0f73f7708dc32f67c2cecfa1dff8380c77 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 30 Sep 2022 08:52:38 +0200 Subject: [PATCH 258/327] Fixed crashing asserts due to a bug in the just merged branch --- src/libslic3r/Measure.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 26 +++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index fbf248c0f7..78d3d524e7 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -646,6 +646,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& angle = std::acos(std::abs(normal1.dot(normal2))); } result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO + result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 568d5db3a2..6aebd0fa37 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -858,8 +858,8 @@ void GLGizmoMeasure::render_dimensioning() const double radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - std::pair e1 = m_selected_features.first.feature->get_edge(); - std::pair e2 = m_selected_features.second.feature->get_edge(); + std::pair e1 = f1.get_edge(); + std::pair e2 = f2.get_edge(); if ((e1.second - e1.first).dot(e1_unit) < 0.) std::swap(e1.first, e1.second); @@ -1019,23 +1019,25 @@ void GLGizmoMeasure::render_dimensioning() : *m_measurement_result.distance_strict; point_point(dap.from, dap.to); - const Measure::SurfaceFeature& f1 = *m_selected_features.first.feature; - const Measure::SurfaceFeature& f2 = *m_selected_features.second.feature; - Measure::SurfaceFeatureType ft1 = f1.get_type(); - Measure::SurfaceFeatureType ft2 = f2.get_type(); + const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); + const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); + Measure::SurfaceFeatureType ft1 = f1->get_type(); + Measure::SurfaceFeatureType ft2 = f2->get_type(); + // Order features by type so following conditions are simple. + if (ft2 > ft2) + std::swap(ft1, ft2); // Where needed, draw also the extension of the edge to where the dist is measured: - if ((int(ft1) | int(ft2)) == - (int(Measure::SurfaceFeatureType::Point) | int(Measure::SurfaceFeatureType::Edge))) - point_edge(f1, f2); + if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + point_edge(*f1, *f2); // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) - arc_edge_edge(f1, f2); - if (int(ft1) | int(ft2) == (int(Measure::SurfaceFeatureType::Edge) | int(Measure::SurfaceFeatureType::Plane))) - arc_edge_plane(f1, f2); + arc_edge_edge(*f1, *f2); + if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + arc_edge_plane(*f1, *f2); } From 6990c3faace6258f5b2062ec8927992b2dc7954f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 09:44:22 +0200 Subject: [PATCH 259/327] Measuring - GLGizmoMeasure - Visualization and selection of extra point for edges --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 28 ++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6aebd0fa37..76d2dacd64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -62,7 +62,7 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t std::string ret; switch (type) { case Measure::SurfaceFeatureType::Point: { ret = _u8L("Vertex"); break; } - case Measure::SurfaceFeatureType::Edge: { ret = _u8L("Point on edge"); break; } + case Measure::SurfaceFeatureType::Edge: { ret = (hover_id == POINT_ID) ? _u8L("Center of edge") : _u8L("Point on edge"); break; } case Measure::SurfaceFeatureType::Circle: { ret = (hover_id == POINT_ID) ? _u8L("Center of circle") : _u8L("Point on circle"); break; } case Measure::SurfaceFeatureType::Plane: { ret = _u8L("Point on plane"); break; } default: { assert(false); break; } @@ -335,6 +335,8 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { m_raycasters.insert({ EDGE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID, *m_cylinder.mesh_raycaster) }); + if (m_curr_feature->get_extra_point().has_value()) + m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; } case Measure::SurfaceFeatureType::Circle: @@ -400,7 +402,11 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value() && m_hover_id == POINT_ID) + m_curr_point_on_feature_position = *extra; + else + m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(EDGE_ID, camera, [](const Vec3f& v) { return Vec3f(0.0f, 0.0f, v.z()); }); break; } case Measure::SurfaceFeatureType::Plane: @@ -497,11 +503,25 @@ void GLGizmoMeasure::on_render() case Measure::SurfaceFeatureType::Edge: { const auto& [start, end] = feature.get_edge(); + // render extra point + const std::optional extra = m_curr_feature->get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + // render edge const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(colors.front()); + m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); if (update_raycasters) { auto it = m_raycasters.find(EDGE_ID); @@ -550,13 +570,13 @@ void GLGizmoMeasure::on_render() colors.emplace_back(hover_selection_color()); break; } + case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Circle: { colors.emplace_back((m_hover_id == POINT_ID) ? hover_selection_color() : hovering_color()); colors.emplace_back(hovering_color()); break; } - case Measure::SurfaceFeatureType::Edge: case Measure::SurfaceFeatureType::Plane: { colors.emplace_back(hovering_color()); From bca8597712b162df93fccd8a0eeb59b5a744a72f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 12:06:07 +0200 Subject: [PATCH 260/327] Follow-up of 9b915bdd0985fd3deb7b99f4d3598b63fa5b3b99 - Fixed crash --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 76d2dacd64..5cc4ce24e3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -504,7 +504,7 @@ void GLGizmoMeasure::on_render() { const auto& [start, end] = feature.get_edge(); // render extra point - const std::optional extra = m_curr_feature->get_extra_point(); + const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); From f051847ac6f9cbe47f21c6b4cf2b2b29553fe34f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 12:54:00 +0200 Subject: [PATCH 261/327] Measuring - Added a bunch of utility functions in Measure.hpp --- src/libslic3r/Measure.cpp | 6 ++--- src/libslic3r/Measure.hpp | 32 ++++++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 21 ++++------------ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 78d3d524e7..b27425ea91 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -633,11 +633,11 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } else if (f1.get_type() == SurfaceFeatureType::Plane) { assert(f2.get_type() == SurfaceFeatureType::Plane); - const auto& [idx1, normal1, pt1] = f1.get_plane(); - const auto& [idx2, normal2, pt2] = f2.get_plane(); + const auto [idx1, normal1, pt1] = f1.get_plane(); + const auto [idx2, normal2, pt2] = f2.get_plane(); double angle = 0.; - if (normal1.isApprox(normal2)) { + if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. Eigen::Hyperplane plane(normal1, pt1); result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index b88411a9f6..4ac56a87f3 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -142,6 +142,38 @@ struct MeasurementResult { // Returns distance/angle between two SurfaceFeatures. MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const SurfaceFeature& edge) { + assert(edge.get_type() == SurfaceFeatureType::Edge); + const auto [from, to] = edge.get_edge(); + return edge_direction(from, to); +} + +inline Vec3d plane_normal(const SurfaceFeature& plane) { + assert(plane.get_type() == SurfaceFeatureType::Plane); + return std::get<1>(plane.get_plane()); +} + +inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } +inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } + +inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_parallel(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_perpendicular(edge_direction(f1), plane_normal(f2)); + else + return false; +} + +inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2) { + if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) + return are_perpendicular(edge_direction(f1), edge_direction(f2)); + else if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Plane) + return are_parallel(edge_direction(f1), plane_normal(f2)); + else + return false; +} } // namespace Measure } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5cc4ce24e3..421dfbedd5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -70,11 +70,6 @@ static std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType t return ret; } -static Vec3d edge_direction(const std::pair& e) -{ - return (e.second - e.first).normalized(); -} - static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector>& planes_triangles, int idx) { assert(0 <= idx && idx < (int)planes_triangles.size()); @@ -123,8 +118,6 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) else if (mouse_event.LeftDown()) { if (m_hover_id != -1) { SelectedFeatures selected_features_old = m_selected_features; - - m_mouse_left_down = true; auto item_from_feature = [this]() { @@ -834,7 +827,6 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); }; - auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; @@ -929,7 +921,7 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e1)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } @@ -941,14 +933,12 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), edge_direction(e2)) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; - - auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); @@ -956,8 +946,8 @@ void GLGizmoMeasure::render_dimensioning() const auto& [idx, normal, origin] = p; const Vec3d e1e2 = e.second - e.first; - const double abs_dot = std::abs(normal.dot(edge_direction(e))); - if (abs_dot < EPSILON || std::abs(abs_dot - 1.0) < EPSILON) + const double abs_dot = std::abs(normal.dot(Measure::edge_direction(f1))); + if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; const Eigen::Hyperplane plane(normal, origin); @@ -1052,11 +1042,10 @@ void GLGizmoMeasure::render_dimensioning() if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) point_edge(*f1, *f2); - // Now if there is an angle to show, draw the arc: if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Edge) arc_edge_edge(*f1, *f2); - if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) + else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); } From cbd228731a180c50fa25d31f7db5483df7638d8e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 13:18:17 +0200 Subject: [PATCH 262/327] Measuring - Refactoring and bug fixing in GLGizmoMeasure::render_dimensioning() --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 36 +++++++++++------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 421dfbedd5..9a62a8c39f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -828,7 +828,8 @@ void GLGizmoMeasure::render_dimensioning() }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { - std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); + assert(f1.get_type() == Measure::SurfaceFeatureType::Point && f2.get_type() == Measure::SurfaceFeatureType::Edge); + const std::pair e = f2.get_edge(); const Vec3d v_proj = m_measurement_result.distance_infinite->to; const Vec3d e1e2 = e.second - e.first; @@ -862,7 +863,8 @@ void GLGizmoMeasure::render_dimensioning() }; auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { - Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; const Vec3d e1_unit = res.angle->e1; @@ -940,13 +942,9 @@ void GLGizmoMeasure::render_dimensioning() }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { - - std::pair e = f1.get_type() == Measure::SurfaceFeatureType::Edge ? f1.get_edge() : f2.get_edge(); - std::tuple p = f1.get_type() == Measure::SurfaceFeatureType::Plane ? f1.get_plane() : f2.get_plane(); - - const auto& [idx, normal, origin] = p; - const Vec3d e1e2 = e.second - e.first; - const double abs_dot = std::abs(normal.dot(Measure::edge_direction(f1))); + assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); + std::pair e = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; @@ -956,21 +954,20 @@ void GLGizmoMeasure::render_dimensioning() // ensure the edge is pointing away from the intersection std::pair ecopy = e; - Vec3d e1e2copy = e1e2; - if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) { + if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) std::swap(ecopy.first, ecopy.second); - e1e2copy = -e1e2copy; - } // calculate 2nd edge (on the plane) - const Vec3d temp = normal.cross(e1e2copy); + const Vec3d e1e2 = ecopy.second - ecopy.first; + const double e1e2_len = e1e2.norm(); + const Vec3d temp = normal.cross(e1e2); const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - std::pair edge_on_plane = { origin, origin + e1e2copy.norm() * edge_on_plane_unit }; + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; // ensure the 2nd edge is pointing in the correct direction - const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2copy); + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); if (test_edge.dot(temp) < 0.0) - edge_on_plane = { origin, origin - e1e2copy.norm() * edge_on_plane_unit }; + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); @@ -1035,8 +1032,10 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeatureType ft2 = f2->get_type(); // Order features by type so following conditions are simple. - if (ft2 > ft2) + if (ft1 > ft2) { std::swap(ft1, ft2); + std::swap(f1, f2); + } // Where needed, draw also the extension of the edge to where the dist is measured: if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) @@ -1048,7 +1047,6 @@ void GLGizmoMeasure::render_dimensioning() else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); } - glsafe(::glEnable(GL_DEPTH_TEST)); From 1c084c4f62b9919f56be3a23b76fb795aab54df4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 14:13:17 +0200 Subject: [PATCH 263/327] Measuring - Some refactoring --- src/libslic3r/Measure.cpp | 21 ++++++++++----------- src/libslic3r/Measure.hpp | 7 +++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index b27425ea91..ff07aa0810 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -250,7 +250,7 @@ void MeasuringImpl::extract_features() // Add the circle and remember indices into borders. const auto& [center, radius] = get_center_and_radius(border, start_idx, i, trafo); circles_idxs.emplace_back(start_idx, i); - circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::optional(), radius)); + circles.emplace_back(SurfaceFeature(SurfaceFeatureType::Circle, center, plane.normal, std::nullopt, radius)); circle = false; } } @@ -269,7 +269,7 @@ void MeasuringImpl::extract_features() const Vec3d center = std::get<0>(circles[i].get_circle()); for (int j=(int)circles_idxs[i].first + 1; j<=(int)circles_idxs[i].second; ++j) plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, - border[j-1], border[j], std::make_optional(center), 0.)); + border[j - 1], border[j], std::make_optional(center))); } else { // This will be handled just like a regular edge. circles_idxs.erase(circles_idxs.begin() + i); @@ -288,8 +288,8 @@ void MeasuringImpl::extract_features() for (int i=1; i (int)circles_idxs[cidx].first) i = circles_idxs[cidx++].second; - else plane.surface_features.emplace_back(SurfaceFeature( - SurfaceFeatureType::Edge, border[i-1], border[i], std::optional(), 0.)); + else + plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Edge, border[i - 1], border[i])); } // FIXME Throw away / do not create edges which are parts of circles or @@ -307,7 +307,7 @@ void MeasuringImpl::extract_features() // The last surface feature is the plane itself. plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - plane.normal, plane.borders.front().front(), std::optional(), i + 0.0001)); + plane.normal, plane.borders.front().front(), std::nullopt, i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -433,13 +433,12 @@ std::vector> Measuring::get_planes_triangle_indices() const static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) { - Vec3d e1_unit = (e1.second - e1.first).normalized(); - Vec3d e2_unit = (e2.second - e2.first).normalized(); - const double dot = e1_unit.dot(e2_unit); - // are edges parallel ? - if (std::abs(std::abs(dot) - 1.0) < EPSILON) + if (are_parallel(e1, e2)) return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + Vec3d e1_unit = edge_direction(e1.first, e1.second); + Vec3d e2_unit = edge_direction(e2.first, e2.second); + // project edges on the plane defined by them Vec3d normal = e1_unit.cross(e2_unit).normalized(); const Eigen::Hyperplane plane(normal, e1.first); @@ -562,7 +561,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& std::vector distances; auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second, std::optional(), 0.)); + const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); double distance = res.distance_strict->dist; Vec3d v2 = res.distance_strict->to; diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 4ac56a87f3..1c51289e8d 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -25,8 +25,8 @@ enum class SurfaceFeatureType : int { class SurfaceFeature { public: - SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3, double value) - : m_type{type}, m_pt1{pt1}, m_pt2{pt2}, m_pt3{pt3}, m_value{value} {} + SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) + : m_type{ type }, m_pt1{ pt1 }, m_pt2{ pt2 }, m_pt3{ pt3 }, m_value{ value } {} explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} @@ -157,6 +157,9 @@ inline Vec3d plane_normal(const SurfaceFeature& plane) { inline bool are_parallel(const Vec3d& v1, const Vec3d& v2) { return std::abs(std::abs(v1.dot(v2)) - 1.0) < EPSILON; } inline bool are_perpendicular(const Vec3d& v1, const Vec3d& v2) { return std::abs(v1.dot(v2)) < EPSILON; } +inline bool are_parallel(const std::pair& e1, const std::pair& e2) { + return are_parallel(e1.second - e1.first, e2.second - e2.first); +} inline bool are_parallel(const SurfaceFeature& f1, const SurfaceFeature& f2) { if (f1.get_type() == SurfaceFeatureType::Edge && f2.get_type() == SurfaceFeatureType::Edge) return are_parallel(edge_direction(f1), edge_direction(f2)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 9a62a8c39f..d6488b83a9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -887,8 +887,6 @@ void GLGizmoMeasure::render_dimensioning() double draw_radius = force_radius ? *force_radius : radius; - const Vec3d normal = e1_unit.cross(e2_unit).normalized(); - if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; @@ -898,6 +896,7 @@ void GLGizmoMeasure::render_dimensioning() init_data.reserve_indices(resolution + 1); // vertices + indices + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); @@ -943,16 +942,19 @@ void GLGizmoMeasure::render_dimensioning() auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - std::pair e = f1.get_edge(); - const auto [idx, normal, origin] = f2.get_plane(); if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) return; + const std::pair e = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane const Eigen::Hyperplane plane(normal, origin); const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); const Vec3d inters = line.intersectionPoint(plane); - // ensure the edge is pointing away from the intersection + // then verify edge direction and revert it, if needed std::pair ecopy = e; if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) std::swap(ecopy.first, ecopy.second); @@ -971,9 +973,9 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.second, ecopy.first, std::optional(), 0.), - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.second, edge_on_plane.first, std::optional(), 0.), - &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.first, ecopy.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.first, edge_on_plane.second), + &radius); }; From 0a8f500819d8f6d06c2e11a5bd041d12cc3ac181 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 14:35:39 +0200 Subject: [PATCH 264/327] Measuring - struct AngleAndPoints reworked as struct AngleAndEdges --- src/libslic3r/Measure.cpp | 14 +++++++------- src/libslic3r/Measure.hpp | 20 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++---------- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index ff07aa0810..b9fb0f3448 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -427,14 +427,12 @@ std::vector> Measuring::get_planes_triangle_indices() const } +const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; - - - -static AngleAndPoints angle_edge_edge(const std::pair& e1, const std::pair& e2) +static AngleAndEdges angle_edge_edge(std::pair e1, std::pair e2) { if (are_parallel(e1, e2)) - return AngleAndPoints(0.0, e1.first, Vec3d::UnitX(), Vec3d::UnitX(), 0., true); + return AngleAndEdges::Dummy; Vec3d e1_unit = edge_direction(e1.first, e1.second); Vec3d e2_unit = edge_direction(e2.first, e2.second); @@ -474,10 +472,12 @@ static AngleAndPoints angle_edge_edge(const std::pair& e1, const s // ensure the edges are pointing away from the center if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { std::swap(e11_proj, e12_proj); + std::swap(e1.first, e1.second); e1_unit = -e1_unit; } if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { std::swap(e21_proj, e22_proj); + std::swap(e2.first, e2.second); e2_unit = -e2_unit; } @@ -488,7 +488,7 @@ static AngleAndPoints angle_edge_edge(const std::pair& e1, const s const Vec3d e2_proj_mid = 0.5 * (e21_proj + e22_proj); const double radius = std::min((center - e1_proj_mid).norm(), (center - e2_proj_mid).norm()); - return AngleAndPoints(angle, center, e1_unit, e2_unit, radius, coplanar); + return { angle, center, e1, e2, radius, coplanar }; } @@ -644,7 +644,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = std::make_optional(AngleAndPoints(angle, Vec3d::Zero(), Vec3d::UnitX(), Vec3d::UnitX(), 0., false)); // TODO + result.angle = std::make_optional(AngleAndEdges(angle, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0., false)); // TODO result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 1c51289e8d..c59c9bbbc7 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -26,7 +26,7 @@ enum class SurfaceFeatureType : int { class SurfaceFeature { public: SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional pt3 = std::nullopt, double value = 0.0) - : m_type{ type }, m_pt1{ pt1 }, m_pt2{ pt2 }, m_pt3{ pt3 }, m_value{ value } {} + : m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {} explicit SurfaceFeature(const Vec3d& pt) : m_type{SurfaceFeatureType::Point}, m_pt1{pt} {} @@ -117,19 +117,21 @@ struct DistAndPoints { Vec3d to; }; -struct AngleAndPoints { - AngleAndPoints(double angle_, Vec3d center_, Vec3d e1_, Vec3d e2_, double radius_, bool coplanar_) - : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} +struct AngleAndEdges { + AngleAndEdges(double angle_, const Vec3d& center_, const std::pair& e1_, const std::pair& e2_, double radius_, bool coplanar_) + : angle(angle_), center(center_), e1(e1_), e2(e2_), radius(radius_), coplanar(coplanar_) {} double angle; Vec3d center; - Vec3d e1; - Vec3d e2; + std::pair e1; + std::pair e2; double radius; bool coplanar; + + static const AngleAndEdges Dummy; }; struct MeasurementResult { - std::optional angle; + std::optional angle; std::optional distance_infinite; std::optional distance_strict; std::optional distance_xyz; @@ -143,10 +145,10 @@ struct MeasurementResult { MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } +inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } inline Vec3d edge_direction(const SurfaceFeature& edge) { assert(edge.get_type() == SurfaceFeatureType::Edge); - const auto [from, to] = edge.get_edge(); - return edge_direction(from, to); + return edge_direction(edge.get_edge()); } inline Vec3d plane_normal(const SurfaceFeature& plane) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d6488b83a9..138e8c54a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -867,19 +867,11 @@ void GLGizmoMeasure::render_dimensioning() const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; - const Vec3d e1_unit = res.angle->e1; - const Vec3d e2_unit = res.angle->e2; + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; const double radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - std::pair e1 = f1.get_edge(); - std::pair e2 = f2.get_edge(); - - if ((e1.second - e1.first).dot(e1_unit) < 0.) - std::swap(e1.first, e1.second); - if ((e2.second - e2.first).dot(e2_unit) < 0.) - std::swap(e2.first, e2.second); - if (radius == 0.) return; @@ -887,6 +879,9 @@ void GLGizmoMeasure::render_dimensioning() double draw_radius = force_radius ? *force_radius : radius; + const Vec3d e1_unit = Measure::edge_direction(e1); + const Vec3d e2_unit = Measure::edge_direction(e2); + if (!m_dimensioning.arc.is_initialized()) { const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; From 3f8820bf203bab125864ef3d0159a4f3883d36ca Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 30 Sep 2022 15:50:31 +0200 Subject: [PATCH 265/327] Measuring - Calculation of angle between edge and plane moved to backend --- src/libslic3r/Measure.cpp | 48 ++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 52 ++++++------------------ 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index b9fb0f3448..f2cbb9f640 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -429,7 +429,7 @@ std::vector> Measuring::get_planes_triangle_indices() const const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; -static AngleAndEdges angle_edge_edge(std::pair e1, std::pair e2) +static AngleAndEdges angle_edge_edge(const std::pair& e1, const std::pair& e2) { if (are_parallel(e1, e2)) return AngleAndEdges::Dummy; @@ -470,14 +470,16 @@ static AngleAndEdges angle_edge_edge(std::pair e1, std::pair out_e1 = e1; + std::pair out_e2 = e2; if ((center_rot_2d - e11_rot_2d).squaredNorm() > (center_rot_2d - e12_rot_2d).squaredNorm()) { std::swap(e11_proj, e12_proj); - std::swap(e1.first, e1.second); + std::swap(out_e1.first, out_e1.second); e1_unit = -e1_unit; } if ((center_rot_2d - e21_rot_2d).squaredNorm() > (center_rot_2d - e22_rot_2d).squaredNorm()) { std::swap(e21_proj, e22_proj); - std::swap(e2.first, e2.second); + std::swap(out_e2.first, out_e2.second); e2_unit = -e2_unit; } @@ -488,9 +490,46 @@ static AngleAndEdges angle_edge_edge(std::pair e1, std::pair& e, const std::tuple& p) +{ + const auto& [idx, normal, origin] = p; + const Vec3d e1e2_unit = edge_direction(e); + if (are_parallel(e1e2_unit, normal) || are_perpendicular(e1e2_unit, normal)) + return AngleAndEdges::Dummy; + + // ensure the edge is pointing away from the intersection + // 1st calculate instersection between edge and plane + const Eigen::Hyperplane plane(normal, origin); + const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); + const Vec3d inters = line.intersectionPoint(plane); + + // then verify edge direction and revert it, if needed + Vec3d e1 = e.first; + Vec3d e2 = e.second; + if ((e1 - inters).squaredNorm() > (e2 - inters).squaredNorm()) + std::swap(e1, e2); + + const Vec3d e1e2 = e2 - e1; + const double e1e2_len = e1e2.norm(); + + // calculate 2nd edge (on the plane) + const Vec3d temp = normal.cross(e1e2); + const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); + std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; + + // ensure the 2nd edge is pointing in the correct direction + const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); + if (test_edge.dot(temp) < 0.0) + edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; + + AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); + const Vec3d e1e2copy_mid = 0.5 * (e1 + e2); + ret.radius = (inters - e1e2copy_mid).norm(); + return ret; +} @@ -615,6 +654,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 138e8c54a3..db015bf1c2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -862,22 +862,20 @@ void GLGizmoMeasure::render_dimensioning() } }; - auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, const double* force_radius = nullptr) { + auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const double angle = res.angle->angle; const Vec3d center = res.angle->center; const std::pair e1 = res.angle->e1; const std::pair e2 = res.angle->e2; - const double radius = res.angle->radius; + const double calc_radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - if (radius == 0.) + if (calc_radius == 0.0) return; - assert(force_radius == nullptr || *force_radius > 0.0); - - double draw_radius = force_radius ? *force_radius : radius; + const double draw_radius = (radius > 0.0) ? radius : calc_radius; const Vec3d e1_unit = Measure::edge_direction(e1); const Vec3d e2_unit = Measure::edge_direction(e2); @@ -930,50 +928,24 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * - Geometry::scale_transform({ (coplanar && (force_radius == nullptr)) ? e21center_len : draw_radius, 1.0f, 1.0f })); + Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - if (Measure::are_parallel(f1, f2) || Measure::are_perpendicular(f1, f2)) + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; + const double calc_radius = res.angle->radius; + if (calc_radius == 0.0) return; - const std::pair e = f1.get_edge(); - const auto [idx, normal, origin] = f2.get_plane(); - - // ensure the edge is pointing away from the intersection - // 1st calculate instersection between edge and plane - const Eigen::Hyperplane plane(normal, origin); - const Eigen::ParametrizedLine line = Eigen::ParametrizedLine::Through(e.first, e.second); - const Vec3d inters = line.intersectionPoint(plane); - - // then verify edge direction and revert it, if needed - std::pair ecopy = e; - if ((ecopy.first - inters).squaredNorm() > (ecopy.second - inters).squaredNorm()) - std::swap(ecopy.first, ecopy.second); - - // calculate 2nd edge (on the plane) - const Vec3d e1e2 = ecopy.second - ecopy.first; - const double e1e2_len = e1e2.norm(); - const Vec3d temp = normal.cross(e1e2); - const Vec3d edge_on_plane_unit = normal.cross(temp).normalized(); - std::pair edge_on_plane = { origin, origin + e1e2_len * edge_on_plane_unit }; - - // ensure the 2nd edge is pointing in the correct direction - const Vec3d test_edge = (edge_on_plane.second - edge_on_plane.first).cross(e1e2); - if (test_edge.dot(temp) < 0.0) - edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; - - const Vec3d e1e2copy_mid = 0.5 * (ecopy.second + ecopy.first); - const double radius = (inters - e1e2copy_mid).norm(); - arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, ecopy.first, ecopy.second), - Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, edge_on_plane.first, edge_on_plane.second), - &radius); + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - shader->start_using(); if (!m_dimensioning.line.is_initialized()) { From 00fb180c70d2089df6ab0f1dbff375a92c7b7663 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 09:14:47 +0200 Subject: [PATCH 266/327] Measuring - Gizmo measure shows dimensioning for angle plane-plane --- src/libslic3r/Measure.cpp | 54 ++++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 15 +++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index f2cbb9f640..4d37ea712c 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -306,8 +306,17 @@ void MeasuringImpl::extract_features() } // The last surface feature is the plane itself. + Vec3d cog = Vec3d::Zero(); + size_t counter = 0; + for (const std::vector& b : plane.borders) { + for (size_t i = 1; i < b.size(); ++i) { + cog += b[i]; + ++counter; + } + } + cog /= double(counter); plane.surface_features.emplace_back(SurfaceFeature(SurfaceFeatureType::Plane, - plane.normal, plane.borders.front().front(), std::nullopt, i + 0.0001)); + plane.normal, cog, std::optional(), i + 0.0001)); plane.borders.clear(); plane.borders.shrink_to_fit(); @@ -526,11 +535,48 @@ static AngleAndEdges angle_edge_plane(const std::pair& e, const st edge_on_plane = { origin, origin - e1e2_len * edge_on_plane_unit }; AngleAndEdges ret = angle_edge_edge({ e1, e2 }, edge_on_plane); - const Vec3d e1e2copy_mid = 0.5 * (e1 + e2); - ret.radius = (inters - e1e2copy_mid).norm(); + ret.radius = (inters - 0.5 * (e1 + e2)).norm(); return ret; } +static AngleAndEdges angle_plane_plane(const std::tuple& p1, const std::tuple& p2) +{ + const auto& [idx1, normal1, origin1] = p1; + const auto& [idx2, normal2, origin2] = p2; + + // are planes parallel ? + if (are_parallel(normal1, normal2)) + return AngleAndEdges::Dummy; + + auto intersection_plane_plane = [](const Vec3d& n1, const Vec3d& o1, const Vec3d& n2, const Vec3d& o2) { + Eigen::MatrixXd m(2, 3); + m << n1.x(), n1.y(), n1.z(), n2.x(), n2.y(), n2.z(); + Eigen::VectorXd b(2); + b << o1.dot(n1), o2.dot(n2); + Eigen::VectorXd x = m.colPivHouseholderQr().solve(b); + return std::make_pair(n1.cross(n2).normalized(), Vec3d(x(0), x(1), x(2))); + }; + + // Calculate intersection line between planes + const auto [intersection_line_direction, intersection_line_origin] = intersection_plane_plane(normal1, origin1, normal2, origin2); + + // Project planes' origin on intersection line + const Eigen::ParametrizedLine intersection_line = Eigen::ParametrizedLine(intersection_line_origin, intersection_line_direction); + const Vec3d origin1_proj = intersection_line.projection(origin1); + const Vec3d origin2_proj = intersection_line.projection(origin2); + + // Calculate edges on planes + const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); + const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); + const double edges_angle = std::acos(std::clamp(edge_on_plane1_unit.dot(edge_on_plane2_unit), -1.0, 1.0)); + const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); + const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; + const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; + + AngleAndEdges ret = angle_edge_edge(edge_on_plane1, edge_on_plane2); + ret.radius = radius; + return ret; +} @@ -684,7 +730,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& // Planes are not parallel, calculate angle. angle = std::acos(std::abs(normal1.dot(normal2))); } - result.angle = std::make_optional(AngleAndEdges(angle, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0., false)); // TODO + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db015bf1c2..b790e43306 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -946,6 +946,19 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); + const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); + const std::pair e1 = res.angle->e1; + const std::pair e2 = res.angle->e2; + const double calc_radius = res.angle->radius; + if (calc_radius == 0.0) + return; + + arc_edge_edge(Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e1.first, e1.second), + Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); + }; + shader->start_using(); if (!m_dimensioning.line.is_initialized()) { @@ -1015,6 +1028,8 @@ void GLGizmoMeasure::render_dimensioning() arc_edge_edge(*f1, *f2); else if (ft1 == Measure::SurfaceFeatureType::Edge && ft2 == Measure::SurfaceFeatureType::Plane) arc_edge_plane(*f1, *f2); + else if (ft1 == Measure::SurfaceFeatureType::Plane && ft2 == Measure::SurfaceFeatureType::Plane) + arc_plane_plane(*f1, *f2); } glsafe(::glEnable(GL_DEPTH_TEST)); From ab3eb723c9686674593e952868a29dfd965af653 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 10:50:38 +0200 Subject: [PATCH 267/327] Measuring - Gizmo measure shows value of distance dimensioning in 3D scene --- src/libslic3r/Measure.cpp | 8 ++-- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 51 ++++++++++++++++-------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 4d37ea712c..529c6f1231 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -663,10 +663,10 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& distances.emplace_back((e2.second - e1.first).norm(), e1.first, e2.second); distances.emplace_back((e2.first - e1.second).norm(), e1.second, e2.first); distances.emplace_back((e2.second - e1.second).norm(), e1.second, e2.second); - add_point_edge_distance(e1.first, e2); - add_point_edge_distance(e1.second, e2); - add_point_edge_distance(e2.first, e1); - add_point_edge_distance(e2.second, e1); +// add_point_edge_distance(e1.first, e2); +// add_point_edge_distance(e1.second, e2); +// add_point_edge_distance(e2.first, e1); +// add_point_edge_distance(e2.second, e1); auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints& item1, const DistAndPoints& item2) { return item1.dist < item2.dist; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b790e43306..4efaf67d78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -44,6 +44,20 @@ static const std::string CTRL_STR = #endif //__APPLE__ ; +static std::string format_double(double value) +{ + char buf[1024]; + sprintf(buf, "%.3f", value); + return std::string(buf); +} + +static std::string format_vec3(const Vec3d& v) +{ + char buf[1024]; + sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); + return std::string(buf); +} + static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type) { switch (type) @@ -778,7 +792,7 @@ void GLGizmoMeasure::render_dimensioning() if (shader == nullptr) return; - auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2) { + auto point_point = [this, shader](const Vec3d& v1, const Vec3d& v2, float distance) { if (v1.isApprox(v2)) return; @@ -825,6 +839,23 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q21ss : ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + + if (distance > 0.0) { + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + std::string txt = format_double(distance) + " "; + if (wxGetApp().app_config->get("use_inches") == "1") + txt += _u8L("in"); + else + txt += _u8L("mm"); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); + } }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -872,7 +903,7 @@ void GLGizmoMeasure::render_dimensioning() const double calc_radius = res.angle->radius; const bool coplanar = res.angle->coplanar; - if (calc_radius == 0.0) + if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) return; const double draw_radius = (radius > 0.0) ? radius : calc_radius; @@ -1006,7 +1037,7 @@ void GLGizmoMeasure::render_dimensioning() const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() ? *m_measurement_result.distance_infinite : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to); + point_point(dap.from, dap.to, dap.dist); const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); @@ -1052,20 +1083,6 @@ static void add_strings_row_to_table(ImGuiWrapper& imgui, const std::string& col add_row_to_table([&]() { imgui.text_colored(col_1_color, col_1); }, [&]() { imgui.text_colored(col_2_color, col_2); }); }; -static std::string format_double(double value) -{ - char buf[1024]; - sprintf(buf, "%.3f", value); - return std::string(buf); -} - -static std::string format_vec3(const Vec3d& v) -{ - char buf[1024]; - sprintf(buf, "X: %.3f, Y: %.3f, Z: %.3f", v.x(), v.y(), v.z()); - return std::string(buf); -} - #if ENABLE_MEASURE_GIZMO_DEBUG void GLGizmoMeasure::render_debug_dialog() { From 5127d47fe04eb7ff6283581d62801e5cd442b9ad Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 11:36:08 +0200 Subject: [PATCH 268/327] Measuring - Gizmo measure shows value of angle dimensioning in 3D scene --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 63 ++++++++++++++---------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 4efaf67d78..8a4cd17d94 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -840,22 +840,18 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); - if (distance > 0.0) { - const Vec2d label_position = 0.5 * (v1ss + v2ss); - m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); - m_imgui->set_next_window_bg_alpha(0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - std::string txt = format_double(distance) + " "; - if (wxGetApp().app_config->get("use_inches") == "1") - txt += _u8L("in"); - else - txt += _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); - } + const Vec2d label_position = 0.5 * (v1ss + v2ss); + m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : + format_double(distance) + " " + _u8L("mm"); + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -911,8 +907,11 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e1_unit = Measure::edge_direction(e1); const Vec3d e2_unit = Measure::edge_direction(e2); + const unsigned int resolution = std::max(2, 64 * angle / double(PI)); + const double step = angle / double(resolution); + const Vec3d normal = e1_unit.cross(e2_unit).normalized(); + if (!m_dimensioning.arc.is_initialized()) { - const unsigned int resolution = std::max(2, 64 * angle / double(PI)); GLModel::Geometry init_data; init_data.format = { GLModel::Geometry::EPrimitiveType::LineStrip, GLModel::Geometry::EVertexLayout::P3 }; init_data.color = ColorRGBA::WHITE(); @@ -920,8 +919,6 @@ void GLGizmoMeasure::render_dimensioning() init_data.reserve_indices(resolution + 1); // vertices + indices - const Vec3d normal = e1_unit.cross(e2_unit).normalized(); - const double step = angle / double(resolution); for (unsigned int i = 0; i <= resolution; ++i) { const double a = step * double(i); const Vec3d v = draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(a, normal)) * e1_unit); @@ -943,8 +940,6 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); @@ -955,13 +950,29 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { - const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } + + // world coordinates + const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + + // screen coordinates + const std::array& viewport = camera.get_viewport(); + const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_world, m_volume_matrix, + camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); + + m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); + m_imgui->set_next_window_bg_alpha(0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); + std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; + m_imgui->text(txt); + m_imgui->end(); + ImGui::PopStyleVar(); }; auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1341,7 +1352,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_infinite.has_value()) { + if (measure.distance_infinite.has_value() && measure.distance_infinite->dist > 0.0) { double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1350,7 +1361,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_strict.has_value()) { + if (measure.distance_strict.has_value() && measure.distance_strict->dist > 0.0) { double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1359,7 +1370,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_xyz.has_value()) { + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; From 14fc691b36fffd6a37d943a2fa3cdb3ffad58207 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 12:54:22 +0200 Subject: [PATCH 269/327] Measuring - Gizmo measure shows arrows at endpoints of angle dimensioning --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 35 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 8a4cd17d94..f55874071d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -929,13 +929,30 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.arc.init_from(std::move(init_data)); } - // render arc + // arc const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); m_dimensioning.arc.render(); - // render edge 1 extension + // arrows + auto render_arrow = [this, shader, &camera, &normal, ¢er, &e1_unit, draw_radius, step, resolution](unsigned int endpoint_id) { + const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); + const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); + const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), direction_model) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal) * + Geometry::scale_transform(camera.get_inv_zoom()); + shader->set_uniform("view_model_matrix", view_model_matrix); + m_dimensioning.triangle.render(); + }; + + glsafe(::glDisable(GL_CULL_FACE)); + render_arrow(1); + render_arrow(2); + glsafe(::glEnable(GL_CULL_FACE)); + + // edge 1 extension const Vec3d e11e12 = e1.second - e1.first; const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); @@ -946,7 +963,7 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.line.render(); } - // render edge 2 extension + // edge 2 extension const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { @@ -956,12 +973,13 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.line.render(); } - // world coordinates - const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + // label + // label model coordinates + const Vec3d label_position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); - // screen coordinates + // label screen coordinates const std::array& viewport = camera.get_viewport(); - const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_world, m_volume_matrix, + const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_model, m_volume_matrix, camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); @@ -969,8 +987,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); m_imgui->begin(_L("##angle"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - std::string txt = format_double(Geometry::rad2deg(angle)) + "°"; - m_imgui->text(txt); + m_imgui->text(format_double(Geometry::rad2deg(angle)) + "°"); m_imgui->end(); ImGui::PopStyleVar(); }; From f4304b15c736334a3b9b4f0e522b87ef42bbd176 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 13:53:14 +0200 Subject: [PATCH 270/327] Follow-up of a5a4fc4dcfb3e5f4ea267e137e70e49b89499d1f - Fixed arrows orientations --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f55874071d..ff59ba0a5c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -940,9 +940,10 @@ void GLGizmoMeasure::render_dimensioning() const double angle = (endpoint_id == 1) ? 0.0 : step * double(resolution); const Vec3d position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(angle, normal)) * e1_unit)); const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); + const auto qz = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const auto qx = Eigen::Quaternion::FromTwoVectors(qz * Vec3d::UnitX(), direction_model); const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), direction_model) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal) * - Geometry::scale_transform(camera.get_inv_zoom()); + qx * qz * Geometry::scale_transform(camera.get_inv_zoom()); shader->set_uniform("view_model_matrix", view_model_matrix); m_dimensioning.triangle.render(); }; From afa003f3cbae0bf9558ac8bd851c665492633b99 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 3 Oct 2022 13:59:11 +0200 Subject: [PATCH 271/327] Fixed warnings --- src/libslic3r/Measure.cpp | 21 ++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 529c6f1231..cce63e6465 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -568,7 +568,6 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, // Calculate edges on planes const Vec3d edge_on_plane1_unit = (origin1 - origin1_proj).normalized(); const Vec3d edge_on_plane2_unit = (origin2 - origin2_proj).normalized(); - const double edges_angle = std::acos(std::clamp(edge_on_plane1_unit.dot(edge_on_plane2_unit), -1.0, 1.0)); const double radius = std::max(10.0, std::max((origin1 - origin1_proj).norm(), (origin2 - origin2_proj).norm())); const std::pair edge_on_plane1 = { origin1_proj + radius * edge_on_plane1_unit, origin1_proj + 2.0 * radius * edge_on_plane1_unit }; const std::pair edge_on_plane2 = { origin2_proj + radius * edge_on_plane2_unit, origin2_proj + 2.0 * radius * edge_on_plane2_unit }; @@ -645,16 +644,16 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& if (f2.get_type() == SurfaceFeatureType::Edge) { std::vector distances; - auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { - const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); - double distance = res.distance_strict->dist; - Vec3d v2 = res.distance_strict->to; - - const Vec3d e1e2 = e.second - e.first; - const Vec3d e1v2 = v2 - e.first; - if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) - distances.emplace_back(distance, v, v2); - }; +// auto add_point_edge_distance = [&distances](const Vec3d& v, const std::pair& e) { +// const MeasurementResult res = get_measurement(SurfaceFeature(v), SurfaceFeature(SurfaceFeatureType::Edge, e.first, e.second)); +// double distance = res.distance_strict->dist; +// Vec3d v2 = res.distance_strict->to; +// +// const Vec3d e1e2 = e.second - e.first; +// const Vec3d e1v2 = v2 - e.first; +// if (e1v2.dot(e1e2) >= 0.0 && e1v2.norm() < e1e2.norm()) +// distances.emplace_back(distance, v, v2); +// }; std::pair e1 = f1.get_edge(); std::pair e2 = f2.get_edge(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index ff59ba0a5c..13415877f9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -993,7 +993,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PopStyleVar(); }; - auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_edge_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const std::pair e1 = res.angle->e1; @@ -1006,7 +1006,7 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_plane_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); const std::pair e1 = res.angle->e1; From 29d61277740f2ebcde3dd7d0989348861c6827f3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 08:17:43 +0200 Subject: [PATCH 272/327] Measuring - Fixes in plane-plane measurement - Measurements validation - Fixes in dimensioning rendering --- src/libslic3r/Measure.cpp | 20 +++++++++++--------- src/libslic3r/Measure.hpp | 4 ++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 20 ++++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index cce63e6465..2905f82553 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -719,21 +719,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const auto [idx1, normal1, pt1] = f1.get_plane(); const auto [idx2, normal2, pt2] = f2.get_plane(); - double angle = 0.; if (are_parallel(normal1, normal2)) { // The planes are parallel, calculate distance. - Eigen::Hyperplane plane(normal1, pt1); - result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(pt2), Vec3d::Zero(), Vec3d::Zero()}); - } else { - // Planes are not parallel, calculate angle. - angle = std::acos(std::abs(normal1.dot(normal2))); + const Eigen::Hyperplane plane(normal1, pt1); + result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO } - result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + else + result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane()); } - + // validation + if (result.distance_infinite.has_value() && result.distance_infinite->dist < EPSILON) + result.distance_infinite.reset(); + if (result.distance_strict.has_value() && result.distance_strict->dist < EPSILON) + result.distance_strict.reset(); + if (result.angle.has_value() && std::abs(result.angle->angle) < EPSILON) + result.angle.reset(); return result; } diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index c59c9bbbc7..df148da761 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -136,6 +136,10 @@ struct MeasurementResult { std::optional distance_strict; std::optional distance_xyz; + bool has_distance_data() const { + return distance_infinite.has_value() || distance_strict.has_value(); + } + bool has_any_data() const { return angle.has_value() || distance_infinite.has_value() || distance_strict.has_value() || distance_xyz.has_value(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 13415877f9..5c467e0203 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1062,11 +1062,14 @@ void GLGizmoMeasure::render_dimensioning() glsafe(::glDisable(GL_DEPTH_TEST)); if (m_selected_features.second.feature.has_value()) { - // Render the arrow between the points that the backend passed: - const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() - ? *m_measurement_result.distance_infinite - : *m_measurement_result.distance_strict; - point_point(dap.from, dap.to, dap.dist); + const bool has_distance = m_measurement_result.has_distance_data(); + if (has_distance) { + // Render the arrow between the points that the backend passed: + const Measure::DistAndPoints& dap = m_measurement_result.distance_infinite.has_value() + ? *m_measurement_result.distance_infinite + : *m_measurement_result.distance_strict; + point_point(dap.from, dap.to, dap.dist); + } const Measure::SurfaceFeature* f1 = &(*m_selected_features.first.feature); const Measure::SurfaceFeature* f2 = &(*m_selected_features.second.feature); @@ -1080,7 +1083,7 @@ void GLGizmoMeasure::render_dimensioning() } // Where needed, draw also the extension of the edge to where the dist is measured: - if (ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) + if (has_distance && ft1 == Measure::SurfaceFeatureType::Point && ft2 == Measure::SurfaceFeatureType::Edge) point_edge(*f1, *f2); // Now if there is an angle to show, draw the arc: @@ -1370,7 +1373,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_infinite.has_value() && measure.distance_infinite->dist > 0.0) { + if (measure.distance_infinite.has_value()) { double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; @@ -1379,7 +1382,8 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } - if (measure.distance_strict.has_value() && measure.distance_strict->dist > 0.0) { + if (measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; From 3ef040cba8f49b2c92902d06705324a97e1522c1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 08:45:39 +0200 Subject: [PATCH 273/327] Measuring - Refactoring in GLGizmoMeasure imgui dialog related to units --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5c467e0203..84b4fc0a97 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1232,7 +1232,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string units = use_inches ? _u8L(" (in)") : _u8L(" (mm)"); + const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); if (m_curr_feature.has_value()) { const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); @@ -1263,7 +1263,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } case Measure::SurfaceFeatureType::Circle: @@ -1276,7 +1276,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit radius = ObjectManipulation::mm_to_in * radius; } add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); break; } @@ -1369,7 +1369,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { ImGui::PushID((void*)(intptr_t)1); - add_measure_row_to_table(_u8L("Angle") + _u8L(" (°)"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)), + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1378,7 +1378,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)2); - add_measure_row_to_table(_u8L("Distance Infinite") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1388,7 +1388,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)3); - add_measure_row_to_table(_u8L("Distance Strict") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance), + add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } @@ -1397,7 +1397,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (use_inches) distance = ObjectManipulation::mm_to_in * distance; ImGui::PushID((void*)(intptr_t)4); - add_measure_row_to_table(_u8L("Distance XYZ") + units, ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); } From e37187a5461f3447a7abeaddf0287139f02ce4f7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 11:26:11 +0200 Subject: [PATCH 274/327] Measuring - Gizmo measure shows dimensioning for distance plane-circle --- src/libslic3r/Measure.cpp | 58 +++++++++++++++++------- src/libslic3r/Measure.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 2905f82553..852e9268cc 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -47,6 +47,7 @@ public: std::vector get_all_features() const; std::optional get_feature(size_t face_idx, const Vec3d& point) const; std::vector> get_planes_triangle_indices() const; + const std::vector& get_plane_features(unsigned int plane_id) const; private: void update_planes(); @@ -398,6 +399,11 @@ std::vector> MeasuringImpl::get_planes_triangle_indices() const return out; } +const std::vector& MeasuringImpl::get_plane_features(unsigned int plane_id) const +{ + assert(plane_id < m_planes.size()); + return m_planes[plane_id].surface_features; +} @@ -435,6 +441,10 @@ std::vector> Measuring::get_planes_triangle_indices() const return priv->get_planes_triangle_indices(); } +const std::vector& Measuring::get_plane_features(unsigned int plane_id) const +{ + return priv->get_plane_features(plane_id); +} const AngleAndEdges AngleAndEdges::Dummy = { 0.0, Vec3d::Zero(), { Vec3d::Zero(), Vec3d::Zero() }, { Vec3d::Zero(), Vec3d::Zero() }, 0.0, true }; @@ -583,7 +593,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple& p1, -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b) +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring) { assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef); @@ -604,25 +614,25 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Edge) { - const auto& [s,e] = f2.get_edge(); - Eigen::ParametrizedLine line(s, (e-s).normalized()); - double dist_inf = line.distance(f1.get_point()); - Vec3d proj = line.projection(f1.get_point()); - double len_sq = (e-s).squaredNorm(); - double dist_start_sq = (proj-s).squaredNorm(); - double dist_end_sq = (proj-e).squaredNorm(); + const auto [s,e] = f2.get_edge(); + const Eigen::ParametrizedLine line(s, (e-s).normalized()); + const double dist_inf = line.distance(f1.get_point()); + const Vec3d proj = line.projection(f1.get_point()); + const double len_sq = (e-s).squaredNorm(); + const double dist_start_sq = (proj-s).squaredNorm(); + const double dist_end_sq = (proj-e).squaredNorm(); if (dist_start_sq < len_sq && dist_end_sq < len_sq) { // projection falls on the line - the strict distance is the same as infinite result.distance_strict = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); } else { // the result is the closer of the endpoints - bool s_is_closer = dist_start_sq < dist_end_sq; - result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + dist_inf), f1.get_point(), s_is_closer ? s : e}); + const bool s_is_closer = dist_start_sq < dist_end_sq; + result.distance_strict = std::make_optional(DistAndPoints{std::sqrt(std::min(dist_start_sq, dist_end_sq) + sqr(dist_inf)), f1.get_point(), s_is_closer ? s : e}); } result.distance_infinite = std::make_optional(DistAndPoints{dist_inf, f1.get_point(), proj}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. - const auto& [c, radius, n] = f2.get_circle(); + const auto [c, radius, n] = f2.get_circle(); Eigen::Hyperplane circle_plane(n, c); Vec3d proj = circle_plane.projection(f1.get_point()); double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + @@ -632,7 +642,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - const auto& [idx, normal, pt] = f2.get_plane(); + const auto [idx, normal, pt] = f2.get_plane(); Eigen::Hyperplane plane(normal, pt); result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(f1.get_point()), f1.get_point(), plane.projection(f1.get_point())}); // TODO // TODO: result.distance_strict = @@ -678,7 +688,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const std::pair e = f1.get_edge(); const auto& [center, radius, normal] = f2.get_circle(); const Vec3d e1e2 = (e.second - e.first); - const Vec3d e1e2_unit = (e.second - e.first).normalized(); + const Vec3d e1e2_unit = e1e2.normalized(); std::vector distances; distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict); @@ -709,14 +719,30 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + assert(measuring != nullptr); + + const auto [center, radius, normal1] = f1.get_circle(); + const auto [idx2, normal2, origin2] = f2.get_plane(); + + const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane(normal1, center).absDistance(origin2) < EPSILON; + if (!coplanar) { + const std::vector& plane_features = measuring->get_plane_features(idx2); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) + distances.push_back(*get_measurement(sf, f1).distance_infinite); + } + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Plane) { - assert(f2.get_type() == SurfaceFeatureType::Plane); - const auto [idx1, normal1, pt1] = f1.get_plane(); const auto [idx2, normal2, pt2] = f2.get_plane(); diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index df148da761..2e17eea5c5 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -105,6 +105,9 @@ public: // call too often. std::vector> get_planes_triangle_indices() const; + // Returns the surface features of the plane with the given index + const std::vector& get_plane_features(unsigned int plane_id) const; + private: std::unique_ptr priv; }; @@ -146,7 +149,7 @@ struct MeasurementResult { }; // Returns distance/angle between two SurfaceFeatures. -MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b); +MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr); inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); } inline Vec3d edge_direction(const std::pair& e) { return edge_direction(e.first, e.second); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 84b4fc0a97..31ce08fa74 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -175,7 +175,7 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) } if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) - m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature); + m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get()); return true; } From d561fb97c72930946c7f7b39d3c11ee380d49813 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 4 Oct 2022 12:36:38 +0200 Subject: [PATCH 275/327] Measuring - Gizmo measure shows dimensioning for distance edge-plane --- src/libslic3r/Measure.cpp | 63 +++++++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 39 ++++++++------- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 852e9268cc..ea68503254 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -708,7 +708,45 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to}); /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + assert(measuring != nullptr); + + const auto [from, to] = f1.get_edge(); + const auto [idx, normal, origin] = f2.get_plane(); + + const Vec3d edge_unit = (to - from).normalized(); + if (are_perpendicular(edge_unit, normal)) { + std::vector distances; + const Eigen::Hyperplane plane(normal, origin); + distances.push_back(DistAndPoints{ plane.absDistance(from), from, plane.projection(from) }); + distances.push_back(DistAndPoints{ plane.absDistance(to), to, plane.projection(to) }); + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + else { + const std::vector& plane_features = measuring->get_plane_features(idx); + std::vector distances; + for (const SurfaceFeature& sf : plane_features) { + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); + } + } result.angle = angle_edge_plane(f1.get_edge(), f2.get_plane()); } /////////////////////////////////////////////////////////////////////////// @@ -729,14 +767,23 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& const std::vector& plane_features = measuring->get_plane_features(idx2); std::vector distances; for (const SurfaceFeature& sf : plane_features) { - if (sf.get_type() == SurfaceFeatureType::Edge) - distances.push_back(*get_measurement(sf, f1).distance_infinite); + if (sf.get_type() == SurfaceFeatureType::Edge) { + const auto m = get_measurement(sf, f1); + if (!m.distance_infinite.has_value()) { + distances.clear(); + break; + } + else + distances.push_back(*m.distance_infinite); + } + } + if (!distances.empty()) { + auto it = std::min_element(distances.begin(), distances.end(), + [](const DistAndPoints& item1, const DistAndPoints& item2) { + return item1.dist < item2.dist; + }); + result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } - auto it = std::min_element(distances.begin(), distances.end(), - [](const DistAndPoints& item1, const DistAndPoints& item2) { - return item1.dist < item2.dist; - }); - result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to }); } } /////////////////////////////////////////////////////////////////////////// diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 31ce08fa74..051a7ee40a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -891,13 +891,15 @@ void GLGizmoMeasure::render_dimensioning() auto arc_edge_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2, double radius = 0.0) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Edge); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const double angle = res.angle->angle; - const Vec3d center = res.angle->center; - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; - const bool coplanar = res.angle->coplanar; + if (!m_measurement_result.angle.has_value()) + return; + + const double angle = m_measurement_result.angle->angle; + const Vec3d center = m_measurement_result.angle->center; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + const bool coplanar = m_measurement_result.angle->coplanar; if (std::abs(angle) < EPSILON || std::abs(calc_radius) < EPSILON) return; @@ -993,12 +995,15 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PopStyleVar(); }; - auto arc_edge_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_edge_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Edge && f2.get_type() == Measure::SurfaceFeatureType::Plane); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; + if (!m_measurement_result.angle.has_value()) + return; + + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + if (calc_radius == 0.0) return; @@ -1006,12 +1011,12 @@ void GLGizmoMeasure::render_dimensioning() Measure::SurfaceFeature(Measure::SurfaceFeatureType::Edge, e2.first, e2.second), calc_radius); }; - auto arc_plane_plane = [arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { + auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); - const Measure::MeasurementResult res = Measure::get_measurement(f1, f2); - const std::pair e1 = res.angle->e1; - const std::pair e2 = res.angle->e2; - const double calc_radius = res.angle->radius; + const std::pair e1 = m_measurement_result.angle->e1; + const std::pair e2 = m_measurement_result.angle->e2; + const double calc_radius = m_measurement_result.angle->radius; + if (calc_radius == 0.0) return; From 395cab88baa90f17f26a0d8dfce74d32828ed26a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Oct 2022 14:23:45 +0200 Subject: [PATCH 276/327] Measuring: prototype for uniformly scale a volume by editing the value of the shown distance Fixed conflicts while rebasing to master --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 111 ++++++++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 63 ++++++------- src/slic3r/GUI/ImGuiWrapper.hpp | 4 +- 4 files changed, 110 insertions(+), 69 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 051a7ee40a..3e83418ef4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -220,6 +220,7 @@ void GLGizmoMeasure::data_changed() m_last_plane_idx = -1; m_selected_features.reset(); m_selection_raycasters.clear(); + m_editing_distance = false; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -256,6 +257,7 @@ void GLGizmoMeasure::on_set_state() m_curr_feature.reset(); m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); + m_editing_distance = false; } else { m_mode = EMode::BasicSelection; @@ -840,18 +842,79 @@ void GLGizmoMeasure::render_dimensioning() ss_to_ndc_matrix * Geometry::translation_transform(v2ss_3) * q12ss); m_dimensioning.triangle.render(); + const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; + const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string value_str = format_double(value); + const std::string units = use_inches ? _u8L("in") : _u8L("mm"); + const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + static double edit_value = 0.0; + const Vec2d label_position = 0.5 * (v1ss + v2ss); m_imgui->set_next_window_pos(label_position.x(), viewport[3] - label_position.y(), ImGuiCond_Always, 0.0f, 1.0f); m_imgui->set_next_window_bg_alpha(0.0f); + + if (!m_editing_distance) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + ImGui::AlignTextToFramePadding(); + m_imgui->text(value_str + " " + units); + ImGui::SameLine(); + if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { + m_editing_distance = true; + edit_value = value; + m_imgui->requires_extra_frame(); + } + m_imgui->end(); + ImGui::PopStyleVar(3); + } + + auto perform_scale = [this, value](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + + if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) + ImGui::OpenPopup("distance_popup"); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - m_imgui->begin(_L("##distance"), ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); - const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const std::string txt = use_inches ? format_double(ObjectManipulation::mm_to_in * distance) + " " + _u8L("in") : - format_double(distance) + " " + _u8L("mm"); - m_imgui->text(txt); - m_imgui->end(); - ImGui::PopStyleVar(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); + if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + ImGui::PushItemWidth(value_str_width); + if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Scale"))) { + perform_scale(edit_value, value); + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (m_imgui->button(_u8L("Cancel"))) { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::PopStyleVar(4); }; auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { @@ -1194,7 +1257,10 @@ void GLGizmoMeasure::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("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + if (m_editing_distance) + return; + + m_imgui->begin(_u8L("Measure tool"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar const float win_h = ImGui::GetWindowHeight(); @@ -1341,28 +1407,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit ImGui::TableSetColumnIndex(1); m_imgui->text_colored(col_2_color, col_2); ImGui::TableSetColumnIndex(2); - ImGuiIO& io = ImGui::GetIO(); - const ImTextureID tex_id = io.Fonts->TexID; - assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); - float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); - float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); - const ImFontAtlasCustomRect* const rect = m_imgui->GetTextureCustomRect(ImGui::ClipboardBtnIcon); - const ImVec2 size = { float(rect->Width), float(rect->Height) }; - const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); - const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); - 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.25f, 0.25f, 0.25f, 1.0f }); - if (m_imgui->image_button(tex_id, size, uv0, uv1)) { + if (m_imgui->image_button(ImGui::ClipboardBtnIcon, _L("Copy to clipboard"))) { wxTheClipboard->Open(); wxTheClipboard->SetData(new wxTextDataObject(col_1 + ": " + col_2)); wxTheClipboard->Close(); } - ImGui::PopStyleColor(3); - if (ImGui::IsItemHovered()) { - const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; - m_imgui->tooltip(into_u8(_L("Copy to clipboard")).c_str(), max_tooltip_width); - } }; if (m_selected_features.second.feature.has_value()) { @@ -1373,7 +1422,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit m_imgui->text(_u8L("Measure") + ":"); if (ImGui::BeginTable("Measure", 3)) { if (measure.angle.has_value()) { - ImGui::PushID((void*)(intptr_t)1); + ImGui::PushID("ClipboardAngle"); add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1382,7 +1431,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_infinite->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)2); + ImGui::PushID("ClipboardDistanceInfinite"); add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1392,7 +1441,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit double distance = measure.distance_strict->dist; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)3); + ImGui::PushID("ClipboardDistanceStrict"); add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); @@ -1401,7 +1450,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit Vec3d distance = *measure.distance_xyz; if (use_inches) distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID((void*)(intptr_t)4); + ImGui::PushID("ClipboardDistanceXYZ"); add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); ImGui::PopID(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 7e65b8c89c..72f9098f2b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -118,6 +118,7 @@ class GLGizmoMeasure : public GLGizmoBase KeyAutoRepeatFilter m_ctrl_kar_filter; SelectedFeatures m_selected_features; + bool m_editing_distance{ false }; void update_if_needed(); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index b8c6c463f5..f1be9d2771 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -426,36 +426,6 @@ bool ImGuiWrapper::draw_radio_button(const std::string& name, float size, bool a return pressed; } -bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format) -{ - return ImGui::InputDouble(label.c_str(), const_cast(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal); -} - -bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format) -{ - auto label_utf8 = into_u8(label); - return input_double(label_utf8, value, format); -} - -bool ImGuiWrapper::input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format) -{ - bool value_changed = false; - - ImGui::BeginGroup(); - - for (int i = 0; i < 3; ++i) - { - std::string item_label = (i == 0) ? "X" : ((i == 1) ? "Y" : "Z"); - ImGui::PushID(i); - ImGui::PushItemWidth(width); - value_changed |= ImGui::InputDouble(item_label.c_str(), const_cast(&value(i)), 0.0f, 0.0f, format.c_str()); - ImGui::PopID(); - } - ImGui::EndGroup(); - - return value_changed; -} - bool ImGuiWrapper::checkbox(const wxString &label, bool &value) { auto label_utf8 = into_u8(label); @@ -514,20 +484,20 @@ void ImGuiWrapper::text_wrapped(const wxString &label, float wrap_width) void ImGuiWrapper::tooltip(const char *label, float wrap_width) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 8.0f, 8.0f }); ImGui::BeginTooltip(); ImGui::PushTextWrapPos(wrap_width); ImGui::TextUnformatted(label); ImGui::PopTextWrapPos(); ImGui::EndTooltip(); + ImGui::PopStyleVar(3); } void ImGuiWrapper::tooltip(const wxString &label, float wrap_width) { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(wrap_width); - ImGui::TextUnformatted(label.ToUTF8().data()); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); + tooltip(label.ToUTF8().data(), wrap_width); } ImVec2 ImGuiWrapper::get_slider_icon_size() const @@ -669,6 +639,29 @@ bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags); } +bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip) +{ + const ImGuiIO& io = ImGui::GetIO(); + const ImTextureID tex_id = io.Fonts->TexID; + assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0); + const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth); + const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight); + const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon); + const ImVec2 size = { float(rect->Width), float(rect->Height) }; + const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h); + const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h); + 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.25f, 0.25f, 0.25f, 1.0f }); + const bool res = image_button(tex_id, size, uv0, uv1); + ImGui::PopStyleColor(3); + + if (!tooltip.empty() && ImGui::IsItemHovered()) + this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f); + + return res; +} + bool ImGuiWrapper::combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags) { // this is to force the label to the left of the widget: diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 769deccb81..dc5f5517ce 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -89,9 +89,6 @@ public: bool button(const wxString& label, float width, float height); bool radio_button(const wxString &label, bool active); bool draw_radio_button(const std::string& name, float size, bool active, std::function draw_callback); - bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f"); - bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); - bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool checkbox(const wxString &label, bool &value); void text(const char *label); void text(const std::string &label); @@ -112,6 +109,7 @@ public: bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true, const wxString& tooltip = {}, bool show_edit_btn = true); bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0); + bool image_button(const wchar_t icon, const wxString& tooltip = L""); // Use selection = -1 to not mark any option as selected bool combo(const wxString& label, const std::vector& options, int& selection, ImGuiComboFlags flags = 0); From 75baf95d71484ceee6f6413bd26e2cc677a80e1b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 6 Oct 2022 14:47:25 +0200 Subject: [PATCH 277/327] Measuring: Fixed rendering of point features when the object is scaled --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 157 ++++++++++++----------- 1 file changed, 79 insertions(+), 78 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e83418ef4..a6efc09e78 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -467,93 +467,94 @@ void GLGizmoMeasure::on_render() auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { - switch (feature.get_type()) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(feature_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); - } - break; - } - case Measure::SurfaceFeatureType::Circle: - { - const auto& [center, radius, normal] = feature.get_circle(); - // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(center_matrix); - m_sphere.model.set_color(colors.front()); - m_sphere.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(POINT_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(center_matrix); - } - // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); - set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(CIRCLE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(circle_matrix); - } - break; - } - case Measure::SurfaceFeatureType::Edge: - { - const auto& [start, end] = feature.get_edge(); - // render extra point - const std::optional extra = feature.get_extra_point(); - if (extra.has_value()) { - const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * Geometry::scale_transform(inv_zoom); - set_matrix_uniforms(point_matrix); + const Transform3d model_matrix_scale_inverse = Geometry::Transformation(model_matrix).get_scaling_factor_matrix().inverse(); + switch (feature.get_type()) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + const Vec3d& position = feature.get_point(); + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); if (update_raycasters) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(point_matrix); + it->second->set_transform(feature_matrix); } + break; } - // render edge - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); - m_cylinder.model.set_color(colors.back()); - m_cylinder.model.render(); - if (update_raycasters) { - auto it = m_raycasters.find(EDGE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); + case Measure::SurfaceFeatureType::Circle: + { + const auto& [center, radius, normal] = feature.get_circle(); + // render center + const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(center_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(center_matrix); + } + // render circle + const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + set_matrix_uniforms(circle_matrix); + m_circle.model.set_color(colors.back()); + m_circle.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(circle_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Edge: + { + const auto& [start, end] = feature.get_edge(); + // render extra point + const std::optional extra = feature.get_extra_point(); + if (extra.has_value()) { + const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + set_matrix_uniforms(point_matrix); + m_sphere.model.set_color(colors.front()); + m_sphere.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(POINT_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(point_matrix); + } + } + // render edge + const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + set_matrix_uniforms(feature_matrix); + m_cylinder.model.set_color(colors.back()); + m_cylinder.model.render(); + if (update_raycasters) { + auto it = m_raycasters.find(EDGE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(feature_matrix); + } + break; + } + case Measure::SurfaceFeatureType::Plane: + { + const auto& [idx, normal, pt] = feature.get_plane(); + assert(idx < m_plane_models_cache.size()); + set_matrix_uniforms(model_matrix); + m_plane_models_cache[idx].set_color(colors.front()); + m_plane_models_cache[idx].render(); + if (update_raycasters) { + auto it = m_raycasters.find(PLANE_ID); + if (it != m_raycasters.end() && it->second != nullptr) + it->second->set_transform(model_matrix); + } + break; } - break; - } - case Measure::SurfaceFeatureType::Plane: - { - const auto& [idx, normal, pt] = feature.get_plane(); - assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); - m_plane_models_cache[idx].set_color(colors.front()); - m_plane_models_cache[idx].render(); - if (update_raycasters) { - auto it = m_raycasters.find(PLANE_ID); - if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); } - break; - } - } }; auto hover_selection_color = [this]() { From 7002b94419aa78ff7d4b4b329a965bf05e15c6e7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 08:40:23 +0200 Subject: [PATCH 278/327] Measuring - Gizmo measure disabled for scaled volumes --- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 04ecfae670..817136e7a4 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -68,6 +68,7 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) +#define DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES (1 && ENABLE_MEASURE_GIZMO) #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index a6efc09e78..f2638d0588 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -290,8 +290,12 @@ bool GLGizmoMeasure::on_is_activable() const bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); - if (res) + if (res) { res &= !selection.get_first_volume()->is_sinking(); +#if DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES + res &= Geometry::Transformation(selection.get_first_volume()->world_matrix()).get_scaling_factor().isApprox(Vec3d::Ones()); +#endif // DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES + } return res; } From 908634d5b7c5a8a8d4037f1da23120c53f6f0fee Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 09:57:13 +0200 Subject: [PATCH 279/327] Measuring - Gizmo measure - Disable background fadeout animation when showing imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 1 + src/slic3r/GUI/ImGuiWrapper.cpp | 7 +++++++ src/slic3r/GUI/ImGuiWrapper.hpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index f2638d0588..d18584a870 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -903,6 +903,7 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + m_imgui->disable_background_fadeout_animation(); ImGui::PushItemWidth(value_str_width); if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index f1be9d2771..2d36727849 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1124,6 +1124,13 @@ ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr; } +#if ENABLE_MEASURE_GIZMO +void ImGuiWrapper::disable_background_fadeout_animation() +{ + GImGui->DimBgRatio = 1.0f; +} +#endif // ENABLE_MEASURE_GIZMO + ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) { return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() }); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index dc5f5517ce..8be16fcd0b 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -130,6 +130,10 @@ public: void set_requires_extra_frame() { m_requires_extra_frame = true; } void reset_requires_extra_frame() { m_requires_extra_frame = false; } +#if ENABLE_MEASURE_GIZMO + void disable_background_fadeout_animation(); +#endif // ENABLE_MEASURE_GIZMO + static ImU32 to_ImU32(const ColorRGBA& color); static ImVec4 to_ImVec4(const ColorRGBA& color); static ColorRGBA from_ImU32(const ImU32& color); From 4b2cc2167d32acd20666f5cb69ee444f395535bc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 10:42:01 +0200 Subject: [PATCH 280/327] Measuring - Gizmo measure - Handle [Enter] and [Esc] keys input events in imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 78 +++++++++++++----------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d18584a870..3e1d9824d4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -848,10 +848,10 @@ void GLGizmoMeasure::render_dimensioning() m_dimensioning.triangle.render(); const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; - const double value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; - const std::string value_str = format_double(value); + const double curr_value = use_inches ? ObjectManipulation::mm_to_in * distance : distance; + const std::string curr_value_str = format_double(curr_value); const std::string units = use_inches ? _u8L("in") : _u8L("mm"); - const float value_str_width = 20.0f + ImGui::CalcTextSize(value_str.c_str()).x; + const float value_str_width = 20.0f + ImGui::CalcTextSize(curr_value_str.c_str()).x; static double edit_value = 0.0; const Vec2d label_position = 0.5 * (v1ss + v2ss); @@ -864,37 +864,17 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); m_imgui->begin(std::string("distance"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); ImGui::AlignTextToFramePadding(); - m_imgui->text(value_str + " " + units); + m_imgui->text(curr_value_str + " " + units); ImGui::SameLine(); if (m_imgui->image_button(ImGui::SliderFloatEditBtnIcon, _L("Edit to scale"))) { m_editing_distance = true; - edit_value = value; + edit_value = curr_value; m_imgui->requires_extra_frame(); } m_imgui->end(); ImGui::PopStyleVar(3); } - auto perform_scale = [this, value](double new_value, double old_value) { - if (new_value == old_value || new_value <= 0.0) - return; - - const double ratio = new_value / old_value; - wxGetApp().plater()->take_snapshot(_L("Scale")); - - TransformationType type; - type.set_world(); - type.set_relative(); - type.set_joint(); - - // apply scale - Selection& selection = m_parent.get_selection(); - selection.setup_cache(); - selection.scale(ratio * Vec3d::Ones(), type); - wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot - wxGetApp().obj_manipul()->set_dirty(); - }; - if (m_editing_distance && !ImGui::IsPopupOpen("distance_popup")) ImGui::OpenPopup("distance_popup"); @@ -903,21 +883,51 @@ void GLGizmoMeasure::render_dimensioning() ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 1.0f, 1.0f }); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 4.0f, 0.0f }); if (ImGui::BeginPopupModal("distance_popup", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration)) { + auto perform_scale = [this](double new_value, double old_value) { + if (new_value == old_value || new_value <= 0.0) + return; + + const double ratio = new_value / old_value; + wxGetApp().plater()->take_snapshot(_L("Scale")); + + TransformationType type; + type.set_world(); + type.set_relative(); + type.set_joint(); + + // apply scale + Selection& selection = m_parent.get_selection(); + selection.setup_cache(); + selection.scale(ratio * Vec3d::Ones(), type); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot + wxGetApp().obj_manipul()->set_dirty(); + }; + auto action_exit = [this]() { + m_editing_distance = false; + ImGui::CloseCurrentPopup(); + }; + auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { + perform_scale(new_value, old_value); + action_exit(); + }; + m_imgui->disable_background_fadeout_animation(); ImGui::PushItemWidth(value_str_width); if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } + + // handle keys input + if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) + action_scale(edit_value, curr_value); + else if (ImGui::IsKeyPressedMap(ImGuiKey_Escape)) + action_exit(); + ImGui::SameLine(); - if (m_imgui->button(_u8L("Scale"))) { - perform_scale(edit_value, value); - m_editing_distance = false; - ImGui::CloseCurrentPopup(); - } + if (m_imgui->button(_u8L("Scale"))) + action_scale(edit_value, curr_value); ImGui::SameLine(); - if (m_imgui->button(_u8L("Cancel"))) { - m_editing_distance = false; - ImGui::CloseCurrentPopup(); - } + if (m_imgui->button(_u8L("Cancel"))) + action_exit(); ImGui::EndPopup(); } ImGui::PopStyleVar(4); From 594e91e86a37e96a12c9706c356288a7ce93f279 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 7 Oct 2022 12:42:30 +0200 Subject: [PATCH 281/327] easuring - Gizmo measure - Reworked imgui dialog layout to avoid change in size in dependence of hovered or selected features --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 266 +++++++++++++---------- 1 file changed, 151 insertions(+), 115 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 3e1d9824d4..15d6ea761f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -63,7 +63,7 @@ static std::string surface_feature_type_as_string(Measure::SurfaceFeatureType ty switch (type) { default: - case Measure::SurfaceFeatureType::Undef: { return _u8L("Undefined"); } + case Measure::SurfaceFeatureType::Undef: { return _u8L("No feature"); } case Measure::SurfaceFeatureType::Point: { return _u8L("Vertex"); } case Measure::SurfaceFeatureType::Edge: { return _u8L("Edge"); } case Measure::SurfaceFeatureType::Circle: { return _u8L("Circle"); } @@ -1292,6 +1292,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } if (ImGui::BeginTable("Commands", 2)) { + unsigned int row_count = 1; add_row_to_table( [this]() { m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); @@ -1310,93 +1311,121 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } ); - if (m_selected_features.first.feature.has_value()) + if (m_selected_features.first.feature.has_value()) { add_strings_row_to_table(*m_imgui, CTRL_STR + "+" + _u8L("Right mouse button"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } - if (m_mode == EMode::BasicSelection && m_hover_id != -1) + if (m_mode == EMode::BasicSelection && m_hover_id != -1) { add_strings_row_to_table(*m_imgui, CTRL_STR, ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++row_count; + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = row_count; i < 3; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); } const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); - if (m_curr_feature.has_value()) { - const Measure::SurfaceFeatureType feature_type = m_curr_feature->get_type(); + const Measure::SurfaceFeatureType feature_type = m_curr_feature.has_value() ? m_curr_feature->get_type() : Measure::SurfaceFeatureType::Undef; + bool data_text_set = false; + ImGui::Separator(); + if (feature_type != Measure::SurfaceFeatureType::Undef) { if (m_mode == EMode::BasicSelection) { - if (feature_type != Measure::SurfaceFeatureType::Undef) { - ImGui::Separator(); - m_imgui->text(surface_feature_type_as_string(feature_type) + ":"); - if (ImGui::BeginTable("Data", 2)) { - switch (feature_type) - { - default: { assert(false); break; } - case Measure::SurfaceFeatureType::Point: - { - Vec3d position = m_volume_matrix * m_curr_feature->get_point(); - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = m_volume_matrix * from; - to = m_volume_matrix * to; - if (use_inches) { - from = ObjectManipulation::mm_to_in * from; - to = ObjectManipulation::mm_to_in * to; - } - add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - center = m_volume_matrix * center; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) { - center = ObjectManipulation::mm_to_in * center; - radius = ObjectManipulation::mm_to_in * radius; - } - add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = m_volume_matrix * origin; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) - origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - break; - } - } - ImGui::EndTable(); - } - } + m_imgui->text(surface_feature_type_as_string(feature_type)); + data_text_set = true; } else if (m_mode == EMode::ExtendedSelection) { if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { - ImGui::Separator(); - m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id) + ":"); - if (ImGui::BeginTable("Data", 2)) { - Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::EndTable(); - } + m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); + data_text_set = true; } } } + if (!data_text_set) + m_imgui->text(_u8L("No feature")); + + const unsigned int max_data_row_count = 3; + unsigned int data_row_count = 0; + if (ImGui::BeginTable("Data", 2)) { + if (m_mode == EMode::BasicSelection) { + switch (feature_type) + { + default: { assert(false); break; } + case Measure::SurfaceFeatureType::Point: + { + Vec3d position = m_volume_matrix * m_curr_feature->get_point(); + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 1; + break; + } + case Measure::SurfaceFeatureType::Edge: + { + auto [from, to] = m_curr_feature->get_edge(); + from = m_volume_matrix * from; + to = m_volume_matrix * to; + if (use_inches) { + from = ObjectManipulation::mm_to_in * from; + to = ObjectManipulation::mm_to_in * to; + } + add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 3; + break; + } + case Measure::SurfaceFeatureType::Circle: + { + auto [center, radius, normal] = m_curr_feature->get_circle(); + center = m_volume_matrix * center; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) { + center = ObjectManipulation::mm_to_in * center; + radius = ObjectManipulation::mm_to_in * radius; + } + add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 3; + break; + } + case Measure::SurfaceFeatureType::Plane: + { + auto [idx, normal, origin] = m_curr_feature->get_plane(); + origin = m_volume_matrix * origin; + normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + if (use_inches) + origin = ObjectManipulation::mm_to_in * origin; + add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 2; + break; + } + } + } + else { + if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { + Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; + if (use_inches) + position = ObjectManipulation::mm_to_in * position; + add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + data_row_count = 1; + } + } + + // add dummy rows to keep dialog size fixed + for (unsigned int i = data_row_count; i < max_data_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); + } ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; @@ -1430,52 +1459,59 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit } }; - if (m_selected_features.second.feature.has_value()) { - const Measure::MeasurementResult& measure = m_measurement_result; + ImGui::Separator(); + m_imgui->text(_u8L("Measure")); - ImGui::Separator(); - if (measure.has_any_data()) { - m_imgui->text(_u8L("Measure") + ":"); - if (ImGui::BeginTable("Measure", 3)) { - if (measure.angle.has_value()) { - ImGui::PushID("ClipboardAngle"); - add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_infinite.has_value()) { - double distance = measure.distance_infinite->dist; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceInfinite"); - add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_strict.has_value() && - (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { - double distance = measure.distance_strict->dist; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceStrict"); - add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { - Vec3d distance = *measure.distance_xyz; - if (use_inches) - distance = ObjectManipulation::mm_to_in * distance; - ImGui::PushID("ClipboardDistanceXYZ"); - add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), - ImGui::GetStyleColorVec4(ImGuiCol_Text)); - ImGui::PopID(); - } - ImGui::EndTable(); + const unsigned int max_measure_row_count = 2; + unsigned int measure_row_count = 0; + if (ImGui::BeginTable("Measure", 4)) { + if (m_selected_features.second.feature.has_value()) { + const Measure::MeasurementResult& measure = m_measurement_result; + if (measure.angle.has_value()) { + ImGui::PushID("ClipboardAngle"); + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_infinite.has_value()) { + double distance = measure.distance_infinite->dist; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceInfinite"); + add_measure_row_to_table(_u8L("Distance Infinite"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_strict.has_value() && + (!measure.distance_infinite.has_value() || std::abs(measure.distance_strict->dist - measure.distance_infinite->dist) > EPSILON)) { + double distance = measure.distance_strict->dist; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceStrict"); + add_measure_row_to_table(_u8L("Distance Strict"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(distance) + units, + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); + } + if (measure.distance_xyz.has_value() && measure.distance_xyz->norm() > EPSILON) { + Vec3d distance = *measure.distance_xyz; + if (use_inches) + distance = ObjectManipulation::mm_to_in * distance; + ImGui::PushID("ClipboardDistanceXYZ"); + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(distance), + ImGui::GetStyleColorVec4(ImGuiCol_Text)); + ++measure_row_count; + ImGui::PopID(); } } - else - m_imgui->text(_u8L("No measure available")); + + // add dummy rows to keep dialog size fixed + for (unsigned int i = measure_row_count; i < max_measure_row_count; ++i) { + add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + } + ImGui::EndTable(); } if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) { From 0b85569c3d1b99b6d2bf13c5af680d5bd7bcec16 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 08:29:51 +0200 Subject: [PATCH 282/327] Follow-up of 2fb59e66c2b6f07a25cfa873fdeb1d820b2d78c4 - Removed obsolete assert --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 15d6ea761f..79a800bf2e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1356,7 +1356,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (m_mode == EMode::BasicSelection) { switch (feature_type) { - default: { assert(false); break; } + default: { break; } case Measure::SurfaceFeatureType::Point: { Vec3d position = m_volume_matrix * m_curr_feature->get_point(); From 6971b727667a1717bc4ed92b2e9c4e8fa7b83581 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 09:46:12 +0200 Subject: [PATCH 283/327] Measuring - Gizmo measure - Auto-select text when opening imgui modal dialog to edit distance --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 10 ++++++++++ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 11 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 79a800bf2e..b4c35b1563 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -221,6 +221,7 @@ void GLGizmoMeasure::data_changed() m_selected_features.reset(); m_selection_raycasters.clear(); m_editing_distance = false; + m_is_editing_distance_first_frame = true; } bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) @@ -258,6 +259,7 @@ void GLGizmoMeasure::on_set_state() m_curr_point_on_feature_position.reset(); restore_scene_raycasters_state(); m_editing_distance = false; + m_is_editing_distance_first_frame = true; } else { m_mode = EMode::BasicSelection; @@ -904,6 +906,7 @@ void GLGizmoMeasure::render_dimensioning() }; auto action_exit = [this]() { m_editing_distance = false; + m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { @@ -916,6 +919,13 @@ void GLGizmoMeasure::render_dimensioning() if (ImGui::InputDouble("##distance", &edit_value, 0.0f, 0.0f, "%.3f")) { } + // trick to auto-select text in the input widgets on 1st frame + if (m_is_editing_distance_first_frame) { + ImGui::SetKeyboardFocusHere(0); + m_is_editing_distance_first_frame = false; + m_imgui->set_requires_extra_frame(); + } + // handle keys input if (ImGui::IsKeyPressedMap(ImGuiKey_Enter) || ImGui::IsKeyPressedMap(ImGuiKey_KeyPadEnter)) action_scale(edit_value, curr_value); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 72f9098f2b..8200325f19 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -119,6 +119,7 @@ class GLGizmoMeasure : public GLGizmoBase SelectedFeatures m_selected_features; bool m_editing_distance{ false }; + bool m_is_editing_distance_first_frame{ true }; void update_if_needed(); From bc1e5a027255c2645805fea01bfb6a181048adad Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 10 Oct 2022 10:12:14 +0200 Subject: [PATCH 284/327] Follow-up of 8312dc24547df8e622bde814efb1b3e0b15bab40 - Fixed rendering of point on locked features when the object is scaled --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 34 ++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b4c35b1563..1976cbcdab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -317,7 +317,6 @@ void GLGizmoMeasure::on_render() (selection.is_single_volume() || selection.is_single_volume_instance())) { update_if_needed(); - m_volume_matrix = selection.get_first_volume()->world_matrix(); const Camera& camera = wxGetApp().plater()->get_camera(); const float inv_zoom = (float)camera.get_inv_zoom(); @@ -397,7 +396,6 @@ void GLGizmoMeasure::on_render() Vec3f n; const Transform3d& trafo = it->second->get_transform(); bool res = it->second->get_raycaster()->closest_hit(m_mouse_pos, trafo, camera, p, n); - assert(res); if (res) { if (callback) p = callback(p); @@ -472,15 +470,14 @@ void GLGizmoMeasure::on_render() }; auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - const Transform3d& model_matrix, float inv_zoom, bool update_raycasters) { - const Transform3d model_matrix_scale_inverse = Geometry::Transformation(model_matrix).get_scaling_factor_matrix().inverse(); + float inv_zoom, bool update_raycasters) { switch (feature.get_type()) { default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(position) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d feature_matrix = m_volume_matrix * Geometry::translation_transform(position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -495,7 +492,7 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = feature.get_circle(); // render center - const Transform3d center_matrix = model_matrix * Geometry::translation_transform(center) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d center_matrix = m_volume_matrix * Geometry::translation_transform(center) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -505,7 +502,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = model_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); m_circle.model.set_color(colors.back()); m_circle.model.render(); @@ -522,7 +519,7 @@ void GLGizmoMeasure::on_render() // render extra point const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { - const Transform3d point_matrix = model_matrix * Geometry::translation_transform(*extra) * model_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Transform3d point_matrix = m_volume_matrix * Geometry::translation_transform(*extra) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -533,16 +530,16 @@ void GLGizmoMeasure::on_render() } } // render edge - const Transform3d feature_matrix = model_matrix * Geometry::translation_transform(start) * + const Transform3d edge_matrix = m_volume_matrix * Geometry::translation_transform(start) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); - set_matrix_uniforms(feature_matrix); + set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); if (update_raycasters) { auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(feature_matrix); + it->second->set_transform(edge_matrix); } break; } @@ -550,13 +547,13 @@ void GLGizmoMeasure::on_render() { const auto& [idx, normal, pt] = feature.get_plane(); assert(idx < m_plane_models_cache.size()); - set_matrix_uniforms(model_matrix); + set_matrix_uniforms(m_volume_matrix); m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].render(); if (update_raycasters) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) - it->second->set_transform(model_matrix); + it->second->set_transform(m_volume_matrix); } break; } @@ -601,13 +598,13 @@ void GLGizmoMeasure::on_render() } } - render_feature(*m_curr_feature, colors, m_volume_matrix, inv_zoom, true); + render_feature(*m_curr_feature, colors, inv_zoom, true); } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { std::vector colors; colors.emplace_back(SELECTED_1ST_COLOR); - render_feature(*m_selected_features.first.feature, colors, m_volume_matrix, inv_zoom, false); + render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_1_ID; }); @@ -618,7 +615,7 @@ void GLGizmoMeasure::on_render() if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { std::vector colors; colors.emplace_back(SELECTED_2ND_COLOR); - render_feature(*m_selected_features.second.feature, colors, m_volume_matrix, inv_zoom, false); + render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), [](std::shared_ptr item) { return SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, item->get_id()) == SELECTION_2_ID; }); @@ -629,7 +626,7 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * Geometry::scale_transform(inv_zoom); + const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -675,6 +672,9 @@ void GLGizmoMeasure::update_if_needed() } m_old_model_object = object; m_old_model_volume = volume; + + m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + m_volume_matrix_scale_inverse = Geometry::Transformation(m_volume_matrix).get_scaling_factor_matrix().inverse(); }; const ModelObject* mo = m_c->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 8200325f19..401feca5db 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,6 +87,7 @@ class GLGizmoMeasure : public GLGizmoBase Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; + Transform3d m_volume_matrix_scale_inverse{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::vector> m_selection_raycasters; From ada7618ddb1575a48a815e619efad0de6d747885 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 10:47:20 +0200 Subject: [PATCH 285/327] Measuring: Gizmo measure shows dimensioning for distance circle-circle --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Measure.cpp | 241 +++++++++++++++++++- src/libslic3r/Measure.hpp | 4 + src/libslic3r/MeasureUtils.hpp | 390 +++++++++++++++++++++++++++++++++ 4 files changed, 632 insertions(+), 4 deletions(-) create mode 100644 src/libslic3r/MeasureUtils.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a52848b229..79e0e6355b 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -182,6 +182,7 @@ set(SLIC3R_SOURCES MeshNormals.cpp Measure.hpp Measure.cpp + MeasureUtils.hpp CustomGCode.cpp CustomGCode.hpp Arrange.hpp diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index ea68503254..27a4f8a90d 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1,10 +1,11 @@ #include "libslic3r/libslic3r.h" #include "Measure.hpp" +#include "MeasureUtils.hpp" #include "libslic3r/Geometry/Circle.hpp" #include "libslic3r/SurfaceMesh.hpp" - +#if ENABLE_MEASURE_GIZMO namespace Slic3r { namespace Measure { @@ -13,8 +14,6 @@ namespace Measure { constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it constexpr double edge_endpoint_limit = 0.5; // how close to an edge endpoint the mouse ... - - static std::pair get_center_and_radius(const std::vector& border, int start_idx, int end_idx, const Transform3d& trafo) { Vec2ds pts; @@ -754,7 +753,238 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& /////////////////////////////////////////////////////////////////////////// } else if (f1.get_type() == SurfaceFeatureType::Circle) { if (f2.get_type() == SurfaceFeatureType::Circle) { - result.distance_infinite = std::make_optional(DistAndPoints{0., Vec3d::Zero(), Vec3d::Zero()}); // TODO + const auto [c0, r0, n0] = f1.get_circle(); + const auto [c1, r1, n1] = f2.get_circle(); + + // The following code is an adaptation of the algorithm found in: + // https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h + // and described in: + // https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf + + struct ClosestInfo + { + double sqrDistance{ 0.0 }; + Vec3d circle0Closest{ Vec3d::Zero() }; + Vec3d circle1Closest{ Vec3d::Zero() }; + + inline bool operator < (const ClosestInfo& other) const { return sqrDistance < other.sqrDistance; } + }; + std::array candidates{}; + + const double zero = 0.0; + + const Vec3d D = c1 - c0; + + if (!are_parallel(n0, n1)) { + auto orthonormal_basis = [](const Vec3d& v) { + std::array ret; + ret[2] = v.normalized(); + int index; + ret[2].maxCoeff(&index); + switch (index) + { + case 0: { ret[0] = Vec3d(ret[2].y(), -ret[2].x(), 0.0).normalized(); break; } + case 1: { ret[0] = Vec3d(0.0, ret[2].z(), -ret[2].y()).normalized(); break; } + case 2: { ret[0] = Vec3d(-ret[2].z(), 0.0, ret[2].x()).normalized(); break; } + } + ret[1] = ret[2].cross(ret[0]).normalized(); + return ret; + }; + + // Get parameters for constructing the degree-8 polynomial phi. + const double one = 1.0; + const double two = 2.0; + const double r0sqr = sqr(r0); + const double r1sqr = sqr(r1); + + // Compute U1 and V1 for the plane of circle1. + const std::array basis = orthonormal_basis(n1); + const Vec3d U1 = basis[0]; + const Vec3d V1 = basis[1]; + + // Construct the polynomial phi(cos(theta)). + const Vec3d N0xD = n0.cross(D); + const Vec3d N0xU1 = n0.cross(U1); + const Vec3d N0xV1 = n0.cross(V1); + const double a0 = r1 * D.dot(U1); + const double a1 = r1 * D.dot(V1); + const double a2 = N0xD.dot(N0xD); + const double a3 = r1 * N0xD.dot(N0xU1); + const double a4 = r1 * N0xD.dot(N0xV1); + const double a5 = r1sqr * N0xU1.dot(N0xU1); + const double a6 = r1sqr * N0xU1.dot(N0xV1); + const double a7 = r1sqr * N0xV1.dot(N0xV1); + Polynomial1 p0{ a2 + a7, two * a3, a5 - a7 }; + Polynomial1 p1{ two * a4, two * a6 }; + Polynomial1 p2{ zero, a1 }; + Polynomial1 p3{ -a0 }; + Polynomial1 p4{ -a6, a4, two * a6 }; + Polynomial1 p5{ -a3, a7 - a5 }; + Polynomial1 tmp0{ one, zero, -one }; + Polynomial1 tmp1 = p2 * p2 + tmp0 * p3 * p3; + Polynomial1 tmp2 = two * p2 * p3; + Polynomial1 tmp3 = p4 * p4 + tmp0 * p5 * p5; + Polynomial1 tmp4 = two * p4 * p5; + Polynomial1 p6 = p0 * tmp1 + tmp0 * p1 * tmp2 - r0sqr * tmp3; + Polynomial1 p7 = p0 * tmp2 + p1 * tmp1 - r0sqr * tmp4; + + // Parameters for polynomial root finding. The roots[] array + // stores the roots. We need only the unique ones, which is + // the responsibility of the set uniqueRoots. The pairs[] + // array stores the (cosine,sine) information mentioned in the + // PDF. TODO: Choose the maximum number of iterations for root + // finding based on specific polynomial data? + const uint32_t maxIterations = 128; + int32_t degree = 0; + size_t numRoots = 0; + std::array roots{}; + std::set uniqueRoots{}; + size_t numPairs = 0; + std::array, 16> pairs{}; + double temp = zero; + double sn = zero; + + if (p7.GetDegree() > 0 || p7[0] != zero) { + // H(cs,sn) = p6(cs) + sn * p7(cs) + Polynomial1 phi = p6 * p6 - tmp0 * p7 * p7; + degree = static_cast(phi.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &phi[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = p7(cs); + if (temp != zero) { + sn = -p6(cs) / temp; + pairs[numPairs++] = std::make_pair(cs, sn); + } + else { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + } + else { + // H(cs,sn) = p6(cs) + degree = static_cast(p6.GetDegree()); + assert(degree > 0); + numRoots = RootsPolynomial::Find(degree, &p6[0], maxIterations, roots.data()); + for (size_t i = 0; i < numRoots; ++i) { + uniqueRoots.insert(roots[i]); + } + + for (auto const& cs : uniqueRoots) { + if (std::fabs(cs) <= one) { + temp = std::max(one - sqr(cs), zero); + sn = std::sqrt(temp); + pairs[numPairs++] = std::make_pair(cs, sn); + if (sn != zero) + pairs[numPairs++] = std::make_pair(cs, -sn); + } + } + } + + for (size_t i = 0; i < numPairs; ++i) { + ClosestInfo& info = candidates[i]; + Vec3d delta = D + r1 * (pairs[i].first * U1 + pairs[i].second * V1); + info.circle1Closest = c0 + delta; + const double N0dDelta = n0.dot(delta); + const double lenN0xDelta = n0.cross(delta).norm(); + if (lenN0xDelta > 0.0) { + const double diff = lenN0xDelta - r0; + info.sqrDistance = sqr(N0dDelta) + sqr(diff); + delta -= N0dDelta * n0; + delta.normalize(); + info.circle0Closest = c0 + r0 * delta; + } + else { + const Vec3d r0U0 = r0 * get_orthogonal(n0, true); + const Vec3d diff = delta - r0U0; + info.sqrDistance = diff.dot(diff); + info.circle0Closest = c0 + r0U0; + } + } + + std::sort(candidates.begin(), candidates.begin() + numPairs); + } + else { + ClosestInfo& info = candidates[0]; + + const double N0dD = n0.dot(D); + const Vec3d normProj = N0dD * n0; + const Vec3d compProj = D - normProj; + Vec3d U = compProj; + const double d = U.norm(); + U.normalize(); + + // The configuration is determined by the relative location of the + // intervals of projection of the circles on to the D-line. + // Circle0 projects to [-r0,r0] and circle1 projects to + // [d-r1,d+r1]. + const double dmr1 = d - r1; + double distance; + if (dmr1 >= r0) { + // d >= r0 + r1 + // The circles are separated (d > r0 + r1) or tangent with one + // outside the other (d = r0 + r1). + distance = dmr1 - r0; + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // d < r0 + r1 + // The cases implicitly use the knowledge that d >= 0. + const double dpr1 = d + r1; + if (dpr1 <= r0) { + // Circle1 is inside circle0. + distance = r0 - dpr1; + if (d > 0.0) { + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else if (dmr1 <= -r0) { + // Circle0 is inside circle1. + distance = -r0 - dmr1; + if (d > 0.0) { + info.circle0Closest = c0 - r0 * U; + info.circle1Closest = c1 - r1 * U; + } + else { + // The circles are concentric, so U = (0,0,0). + // Construct a vector perpendicular to N0 to use for + // closest points. + U = get_orthogonal(n0, true); + info.circle0Closest = c0 + r0 * U; + info.circle1Closest = c1 + r1 * U; + } + } + else { + distance = (c1 - c0).norm(); + info.circle0Closest = c0; + info.circle1Closest = c1; + } + } + + info.sqrDistance = distance * distance + N0dD * N0dD; + } + + result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { assert(measuring != nullptr); @@ -823,3 +1053,6 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } // namespace Measure } // namespace Slic3r + + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index 2e17eea5c5..f310d8f9fa 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,6 +1,8 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ +#if ENABLE_MEASURE_GIZMO + #include #include @@ -191,3 +193,5 @@ inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2 } // namespace Slic3r #endif // Slic3r_Measure_hpp_ + +#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp new file mode 100644 index 0000000000..4f84624eac --- /dev/null +++ b/src/libslic3r/MeasureUtils.hpp @@ -0,0 +1,390 @@ +#ifndef Slic3r_MeasureUtils_hpp_ +#define Slic3r_MeasureUtils_hpp_ + +#if ENABLE_MEASURE_GIZMO + +#include + +namespace Slic3r { +namespace Measure { + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h + +class Polynomial1 +{ +public: + Polynomial1(std::initializer_list values) + { + // C++ 11 will call the default constructor for + // Polynomial1 p{}, so it is guaranteed that + // values.size() > 0. + m_coefficient.resize(values.size()); + std::copy(values.begin(), values.end(), m_coefficient.begin()); + EliminateLeadingZeros(); + } + + // Construction and destruction. The first constructor creates a + // polynomial of the specified degree but sets all coefficients to + // zero (to ensure initialization). You are responsible for setting + // the coefficients, presumably with the degree-term set to a nonzero + // number. In the second constructor, the degree is the number of + // initializers plus 1, but then adjusted so that coefficient[degree] + // is not zero (unless all initializer values are zero). + explicit Polynomial1(uint32_t degree) + : m_coefficient(static_cast(degree) + 1, 0.0) + {} + + // Eliminate any leading zeros in the polynomial, except in the case + // the degree is 0 and the coefficient is 0. The elimination is + // necessary when arithmetic operations cause a decrease in the degree + // of the result. For example, (1 + x + x^2) + (1 + 2*x - x^2) = + // (2 + 3*x). The inputs both have degree 2, so the result is created + // with degree 2. After the addition we find that the degree is in + // fact 1 and resize the array of coefficients. This function is + // called internally by the arithmetic operators, but it is exposed in + // the public interface in case you need it for your own purposes. + void EliminateLeadingZeros() + { + const size_t size = m_coefficient.size(); + if (size > 1) { + const double zero = 0.0; + int32_t leading; + for (leading = static_cast(size) - 1; leading > 0; --leading) { + if (m_coefficient[leading] != zero) + break; + } + + m_coefficient.resize(++leading); + } + } + + // Set all coefficients to the specified value. + void SetCoefficients(double value) + { + std::fill(m_coefficient.begin(), m_coefficient.end(), value); + } + + inline uint32_t GetDegree() const + { + // By design, m_coefficient.size() > 0. + return static_cast(m_coefficient.size() - 1); + } + + inline const double& operator[](uint32_t i) const { return m_coefficient[i]; } + inline double& operator[](uint32_t i) { return m_coefficient[i]; } + + // Evaluate the polynomial. If the polynomial is invalid, the + // function returns zero. + double operator()(double t) const + { + int32_t i = static_cast(m_coefficient.size()); + double result = m_coefficient[--i]; + for (--i; i >= 0; --i) { + result *= t; + result += m_coefficient[i]; + } + return result; + } + +protected: + // The class is designed so that m_coefficient.size() >= 1. + std::vector m_coefficient; +}; + +Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + Polynomial1 result(p0Degree + p1Degree); + result.SetCoefficients(0.0); + for (uint32_t i0 = 0; i0 <= p0Degree; ++i0) { + for (uint32_t i1 = 0; i1 <= p1Degree; ++i1) { + result[i0 + i1] += p0[i0] * p1[i1]; + } + } + return result; +} + +Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] + p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) +{ + const uint32_t p0Degree = p0.GetDegree(); + const uint32_t p1Degree = p1.GetDegree(); + uint32_t i; + if (p0Degree >= p1Degree) { + Polynomial1 result(p0Degree); + for (i = 0; i <= p1Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p0Degree; ++i) { + result[i] = p0[i]; + } + result.EliminateLeadingZeros(); + return result; + } + else { + Polynomial1 result(p1Degree); + for (i = 0; i <= p0Degree; ++i) { + result[i] = p0[i] - p1[i]; + } + for (/**/; i <= p1Degree; ++i) { + result[i] = -p1[i]; + } + result.EliminateLeadingZeros(); + return result; + } +} + +Polynomial1 operator * (double scalar, const Polynomial1& p) +{ + const uint32_t degree = p.GetDegree(); + Polynomial1 result(degree); + for (uint32_t i = 0; i <= degree; ++i) { + result[i] = scalar * p[i]; + } + return result; +} + +// Utility class used to calculate distance circle-circle +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h + +class RootsPolynomial +{ +public: + // General equations: sum_{i=0}^{d} c(i)*t^i = 0. The input array 'c' + // must have at least d+1 elements and the output array 'root' must + // have at least d elements. + + // Find the roots on (-infinity,+infinity). + static int32_t Find(int32_t degree, const double* c, uint32_t maxIterations, double* roots) + { + if (degree >= 0 && c != nullptr) { + const double zero = 0.0; + while (degree >= 0 && c[degree] == zero) { + --degree; + } + + if (degree > 0) { + // Compute the Cauchy bound. + const double one = 1.0; + const double invLeading = one / c[degree]; + double maxValue = zero; + for (int32_t i = 0; i < degree; ++i) { + const double value = std::fabs(c[i] * invLeading); + if (value > maxValue) + maxValue = value; + } + const double bound = one + maxValue; + + return FindRecursive(degree, c, -bound, bound, maxIterations, roots); + } + else if (degree == 0) + // The polynomial is a nonzero constant. + return 0; + else { + // The polynomial is identically zero. + roots[0] = zero; + return 1; + } + } + else + // Invalid degree or c. + return 0; + } + + // If you know that p(tmin) * p(tmax) <= 0, then there must be at + // least one root in [tmin, tmax]. Compute it using bisection. + static bool Find(int32_t degree, const double* c, double tmin, double tmax, uint32_t maxIterations, double& root) + { + const double zero = 0.0; + double pmin = Evaluate(degree, c, tmin); + if (pmin == zero) { + root = tmin; + return true; + } + double pmax = Evaluate(degree, c, tmax); + if (pmax == zero) { + root = tmax; + return true; + } + + if (pmin * pmax > zero) + // It is not known whether the interval bounds a root. + return false; + + if (tmin >= tmax) + // Invalid ordering of interval endpoitns. + return false; + + for (uint32_t i = 1; i <= maxIterations; ++i) { + root = 0.5 * (tmin + tmax); + + // This test is designed for 'float' or 'double' when tmin + // and tmax are consecutive floating-point numbers. + if (root == tmin || root == tmax) + break; + + const double p = Evaluate(degree, c, root); + const double product = p * pmin; + if (product < zero) { + tmax = root; + pmax = p; + } + else if (product > zero) { + tmin = root; + pmin = p; + } + else + break; + } + + return true; + } + + // Support for the Find functions. + static int32_t FindRecursive(int32_t degree, double const* c, double tmin, double tmax, uint32_t maxIterations, double* roots) + { + // The base of the recursion. + const double zero = 0.0; + double root = zero; + if (degree == 1) { + int32_t numRoots; + if (c[1] != zero) { + root = -c[0] / c[1]; + numRoots = 1; + } + else if (c[0] == zero) { + root = zero; + numRoots = 1; + } + else + numRoots = 0; + + if (numRoots > 0 && tmin <= root && root <= tmax) { + roots[0] = root; + return 1; + } + return 0; + } + + // Find the roots of the derivative polynomial scaled by 1/degree. + // The scaling avoids the factorial growth in the coefficients; + // for example, without the scaling, the high-order term x^d + // becomes (d!)*x through multiple differentiations. With the + // scaling we instead get x. This leads to better numerical + // behavior of the root finder. + const int32_t derivDegree = degree - 1; + std::vector derivCoeff(static_cast(derivDegree) + 1); + std::vector derivRoots(derivDegree); + for (int32_t i = 0, ip1 = 1; i <= derivDegree; ++i, ++ip1) { + derivCoeff[i] = c[ip1] * (double)(ip1) / (double)degree; + } + const int32_t numDerivRoots = FindRecursive(degree - 1, &derivCoeff[0], tmin, tmax, maxIterations, &derivRoots[0]); + + int32_t numRoots = 0; + if (numDerivRoots > 0) { + // Find root on [tmin,derivRoots[0]]. + if (Find(degree, c, tmin, derivRoots[0], maxIterations, root)) + roots[numRoots++] = root; + + // Find root on [derivRoots[i],derivRoots[i+1]]. + for (int32_t i = 0, ip1 = 1; i <= numDerivRoots - 2; ++i, ++ip1) { + if (Find(degree, c, derivRoots[i], derivRoots[ip1], maxIterations, root)) + roots[numRoots++] = root; + } + + // Find root on [derivRoots[numDerivRoots-1],tmax]. + if (Find(degree, c, derivRoots[static_cast(numDerivRoots) - 1], tmax, maxIterations, root)) + roots[numRoots++] = root; + } + else { + // The polynomial is monotone on [tmin,tmax], so has at most one root. + if (Find(degree, c, tmin, tmax, maxIterations, root)) + roots[numRoots++] = root; + } + return numRoots; + } + + static double Evaluate(int32_t degree, const double* c, double t) + { + int32_t i = degree; + double result = c[i]; + while (--i >= 0) { + result = t * result + c[i]; + } + return result; + } +}; + +// Adaptation of code found in: +// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h + +// Construct a single vector orthogonal to the nonzero input vector. If +// the maximum absolute component occurs at index i, then the orthogonal +// vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components +// zero. The index addition i+1 is computed modulo N. +Vec3d get_orthogonal(const Vec3d& v, bool unitLength) +{ + double cmax = std::fabs(v[0]); + int32_t imax = 0; + for (int32_t i = 1; i < 3; ++i) { + double c = std::fabs(v[i]); + if (c > cmax) { + cmax = c; + imax = i; + } + } + + Vec3d result = Vec3d::Zero(); + int32_t inext = imax + 1; + if (inext == 3) + inext = 0; + + result[imax] = v[inext]; + result[inext] = -v[imax]; + if (unitLength) { + const double sqrDistance = result[imax] * result[imax] + result[inext] * result[inext]; + const double invLength = 1.0 / std::sqrt(sqrDistance); + result[imax] *= invLength; + result[inext] *= invLength; + } + return result; +} + +} // namespace Slic3r +} // namespace Measure + +#endif // Slic3r_MeasureUtils_hpp_ + +#endif // ENABLE_MEASURE_GIZMO From 1a67da32f247689c649cf6cf89aca4f23cad6f6f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 11:41:15 +0200 Subject: [PATCH 286/327] Fixed warning --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 1976cbcdab..0d53c69385 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -909,7 +909,7 @@ void GLGizmoMeasure::render_dimensioning() m_is_editing_distance_first_frame = true; ImGui::CloseCurrentPopup(); }; - auto action_scale = [this, perform_scale, action_exit](double new_value, double old_value) { + auto action_scale = [perform_scale, action_exit](double new_value, double old_value) { perform_scale(new_value, old_value); action_exit(); }; From dc27dbb6ff72a6b6e22833108ec70f04249c81df Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 11 Oct 2022 13:38:07 +0200 Subject: [PATCH 287/327] Measuring: Gizmo measure - Fixed rendering of selected circle features --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 34 ++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0d53c69385..db33135dda 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -329,7 +329,8 @@ void GLGizmoMeasure::on_render() if (m_mode == EMode::BasicSelection) { std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); - if (m_curr_feature != curr_feature) { + if (m_curr_feature != curr_feature || + (curr_feature.has_value() && curr_feature->get_type() == Measure::SurfaceFeatureType::Circle && (m_curr_feature != curr_feature || m_last_inv_zoom != inv_zoom))) { m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, EDGE_ID); m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID); @@ -470,7 +471,7 @@ void GLGizmoMeasure::on_render() }; auto render_feature = [this, set_matrix_uniforms](const Measure::SurfaceFeature& feature, const std::vector& colors, - float inv_zoom, bool update_raycasters) { + float inv_zoom, bool update_raycasters_transform) { switch (feature.get_type()) { default: { assert(false); break; } @@ -481,7 +482,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(feature_matrix); @@ -496,7 +497,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(center_matrix); @@ -504,13 +505,20 @@ void GLGizmoMeasure::on_render() // render circle const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); set_matrix_uniforms(circle_matrix); - m_circle.model.set_color(colors.back()); - m_circle.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { + m_circle.model.set_color(colors.back()); + m_circle.model.render(); auto it = m_raycasters.find(CIRCLE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(circle_matrix); } + else { + GLModel circle; + GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + circle.init_from(std::move(circle_geometry)); + circle.set_color(colors.back()); + circle.render(); + } break; } case Measure::SurfaceFeatureType::Edge: @@ -523,7 +531,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(POINT_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(point_matrix); @@ -536,7 +544,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(EDGE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(edge_matrix); @@ -550,7 +558,7 @@ void GLGizmoMeasure::on_render() set_matrix_uniforms(m_volume_matrix); m_plane_models_cache[idx].set_color(colors.front()); m_plane_models_cache[idx].render(); - if (update_raycasters) { + if (update_raycasters_transform) { auto it = m_raycasters.find(PLANE_ID); if (it != m_raycasters.end() && it->second != nullptr) it->second->set_transform(m_volume_matrix); @@ -602,8 +610,7 @@ void GLGizmoMeasure::on_render() } if (m_selected_features.first.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.first.feature)) { - std::vector colors; - colors.emplace_back(SELECTED_1ST_COLOR); + const std::vector colors = { SELECTED_1ST_COLOR }; render_feature(*m_selected_features.first.feature, colors, inv_zoom, false); if (m_selected_features.first.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), @@ -613,8 +620,7 @@ void GLGizmoMeasure::on_render() } } if (m_selected_features.second.feature.has_value() && (!m_curr_feature.has_value() || *m_curr_feature != *m_selected_features.second.feature)) { - std::vector colors; - colors.emplace_back(SELECTED_2ND_COLOR); + const std::vector colors = { SELECTED_2ND_COLOR }; render_feature(*m_selected_features.second.feature, colors, inv_zoom, false); if (m_selected_features.second.feature->get_type() == Measure::SurfaceFeatureType::Point) { auto it = std::find_if(m_selection_raycasters.begin(), m_selection_raycasters.end(), From e3db1879b128ecf5af383c7c23a103f5b6300e4d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 Oct 2022 10:05:45 +0200 Subject: [PATCH 288/327] Fixed differences after rebase to master --- src/slic3r/GUI/GLCanvas3D.cpp | 26 ++- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 252 +++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 - 3 files changed, 133 insertions(+), 146 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7d8ca616b4..62912fa5e1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5537,15 +5537,19 @@ void GLCanvas3D::_picking_pass() imgui.text_colored(col_2_color, col_2.c_str()); }; - char buf[1024]; if (hit.type != SceneRaycaster::EType::None) { if (ImGui::BeginTable("Hit", 2)) { - add_strings_row_to_table("Object ID", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(hit.raycaster_id), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table("Type", ImGuiWrapper::COL_ORANGE_LIGHT, object_type, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + char buf[1024]; + add_strings_row_to_table("Object ID:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::to_string(hit.raycaster_id), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); + add_strings_row_to_table("Type:", ImGuiWrapper::COL_ORANGE_LIGHT, + object_type, ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); sprintf(buf, "%.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z()); - add_strings_row_to_table("Position", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table("Position:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); sprintf(buf, "%.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z()); - add_strings_row_to_table("Normal", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table("Normal:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); ImGui::EndTable(); } } @@ -5554,13 +5558,17 @@ void GLCanvas3D::_picking_pass() ImGui::Separator(); imgui.text("Registered for picking:"); - if (ImGui::BeginTable("Raycasters", 2)) { + if (ImGui::BeginTable("Counters", 2)) { + char buf[1024]; sprintf(buf, "%d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count()); - add_strings_row_to_table("Beds", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table("Beds:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); sprintf(buf, "%d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count()); - add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table("Volumes:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count()); - add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_strings_row_to_table("Gizmo elements:", ImGuiWrapper::COL_ORANGE_LIGHT, + std::string(buf), ImGuiWrapper::to_ImVec4(ColorRGBA::WHITE())); ImGui::EndTable(); } imgui.end(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 70bbc92107..3af58dd960 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -10,7 +10,6 @@ #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" -#include "libslic3r/SurfaceMesh.hpp" #include @@ -22,31 +21,9 @@ namespace GUI { static const Slic3r::ColorRGBA DEFAULT_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.5f }; static const Slic3r::ColorRGBA DEFAULT_HOVER_PLANE_COLOR = { 0.9f, 0.9f, 0.9f, 0.75f }; - - - -// TESTING: -static Halfedge_index hi; -static bool hi_initialized = false; -static std::unique_ptr sm_ptr; -static Vertex_index src; -static Vertex_index tgt; - - - - - - GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) -{ - indexed_triangle_set a = its_make_cone(0.05, .2); - its_rotate_x(a, M_PI); - its_translate(a, stl_vertex(0., 0., .8)); - indexed_triangle_set b = its_make_cylinder(.02, 0.8); - its_merge(a, b); - arrow.init_from(a); -} +{} bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) { @@ -60,7 +37,9 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = true; Selection &selection = m_parent.get_selection(); if (selection.is_single_full_instance()) { - hi = sm_ptr->halfedge(Face_index(m_hover_id)); + // Rotate the object so the normal points downward: + selection.flattening_rotate(m_planes[m_hover_id].normal); + m_parent.do_rotate(L("Gizmo-Place on Face")); } return true; } @@ -122,18 +101,6 @@ void GLGizmoFlatten::on_render() const Selection& selection = m_parent.get_selection(); #if ENABLE_LEGACY_OPENGL_REMOVAL - - - - if (!hi_initialized) { - const indexed_triangle_set & its = m_c->selection_info()->model_object()->volumes.front()->mesh().its; - sm_ptr.reset(new SurfaceMesh(its)); - hi = sm_ptr->halfedge(Face_index(0)); - hi_initialized = true; - } - SurfaceMesh & sm = *sm_ptr; - - GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) return; @@ -169,13 +136,8 @@ void GLGizmoFlatten::on_render() m_planes[i].vbo.model.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); m_planes[i].vbo.model.render(); #else - int cur_face = hi.is_invalid() ? 1000000 : sm.face(hi); - for (int i = 0; i < m_planes.size(); ++i) { - m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); - if (i == cur_face) - m_planes[i].vbo.set_color(i == m_hover_id ? ColorRGBA(.5f, 0.f, 0.f, 1.f) : ColorRGBA(1.f, 0.f, 0.f, 1.f)); - m_planes[i].vbo.render(); - } + m_planes[i].vbo.set_color(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR : DEFAULT_PLANE_COLOR); + m_planes[i].vbo.render(); #endif // ENABLE_RAYCAST_PICKING #else glsafe(::glColor4fv(i == m_hover_id ? DEFAULT_HOVER_PLANE_COLOR.data() : DEFAULT_PLANE_COLOR.data())); @@ -188,90 +150,6 @@ void GLGizmoFlatten::on_render() #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } - - - - - ///////////////// - //////////////// - ////////////////// - - auto draw_arrow = [&](const Vec3d& from, const Vec3d& to) -> void { - Vec3d desired_pos = from; - Vec3d desired_dir = to - from; - double desired_len = desired_dir.norm(); - desired_dir.normalize(); - - Transform3d m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix(); - m.translate(desired_pos); - Eigen::Quaterniond q; - Transform3d rot = Transform3d::Identity(); - rot.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), desired_dir).toRotationMatrix(); - Transform3d sc = Transform3d::Identity(); - sc.scale(desired_len); - m = m * sc * rot; - - const Camera & camera = wxGetApp().plater()->get_camera(); - Transform3d view_model_matrix = camera.get_view_matrix() * - Geometry::assemble_transform(selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z() * Vec3d::UnitZ()) * m; - - shader->set_uniform("view_model_matrix", view_model_matrix); - arrow.render(); - }; - - m_imgui->begin(std::string("DEBUG")); - bool invalid = hi.is_invalid(); - if (invalid) { - if (m_imgui->button(std::string("HALFEDGE INVALID (Click to reset)"))) - hi = sm.halfedge(Face_index(0)); - } - else { - m_imgui->text(sm.is_border(hi) ? "BORDER HALFEDGE !" : "Halfedge is not border"); - m_imgui->text((std::string("Face: ") + std::to_string(int(hi.face()))).c_str()); - m_imgui->text(std::string("Target degree:" + std::to_string(sm.degree(sm.target(hi))))); - m_imgui->text(std::string("Face degree:" + std::to_string(sm.degree(sm.face(hi))))); - } - m_imgui->disabled_begin(invalid); - if (m_imgui->button(std::string("next"))) - hi = sm.next(hi); - if (m_imgui->button(std::string("prev"))) - hi = sm.prev(hi); - if (m_imgui->button(std::string("opposite"))) - hi = sm.opposite(hi); - if (m_imgui->button(std::string("next_around_target"))) - hi = sm.next_around_target(hi); - if (m_imgui->button(std::string("prev_around_target"))) - hi = sm.prev_around_target(hi); - if (m_imgui->button(std::string("next_around_source"))) - hi = sm.next_around_source(hi); - if (m_imgui->button(std::string("prev_around_source"))) - hi = sm.prev_around_source(hi); - if (m_imgui->button(std::string("remember one"))) - src = sm.target(hi); - if (m_imgui->button(std::string("switch to halfedge"))) { - tgt = sm.target(hi); - hi = sm.halfedge(src, tgt); - } - - if (invalid) - m_imgui->disabled_end(); - m_imgui->end(); - - if (!hi.is_invalid()) { - Vec3d a = sm.point(sm.source(hi)).cast(); - Vec3d b = sm.point(sm.target(hi)).cast(); - draw_arrow(a, b); - } - - - ///////////////// - //////////////// - ////////////////// - - - - - glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glDisable(GL_BLEND)); @@ -375,11 +253,11 @@ void GLGizmoFlatten::update_planes() for (const ModelVolume* vol : mo->volumes) { if (vol->type() != ModelVolumeType::MODEL_PART) continue; - TriangleMesh vol_ch = vol->mesh(); //vol->get_convex_hull(); + TriangleMesh vol_ch = vol->get_convex_hull(); vol_ch.transform(vol->get_matrix()); ch.merge(vol_ch); } - //ch = ch.convex_hull_3d(); + ch = ch.convex_hull_3d(); m_planes.clear(); #if ENABLE_RAYCAST_PICKING on_unregister_raycasters_for_picking(); @@ -403,17 +281,47 @@ void GLGizmoFlatten::update_planes() std::vector facet_visited(num_of_facets, false); int facet_queue_cnt = 0; const stl_normal* normal_ptr = nullptr; + int facet_idx = 0; + while (1) { + // Find next unvisited triangle: + for (; facet_idx < num_of_facets; ++ facet_idx) + if (!facet_visited[facet_idx]) { + facet_queue[facet_queue_cnt ++] = facet_idx; + facet_visited[facet_idx] = true; + normal_ptr = &face_normals[facet_idx]; + m_planes.emplace_back(); + break; + } + if (facet_idx == num_of_facets) + break; // Everything was visited already + + while (facet_queue_cnt > 0) { + int facet_idx = facet_queue[-- facet_queue_cnt]; + const stl_normal& this_normal = face_normals[facet_idx]; + if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) { + const Vec3i face = ch.its.indices[facet_idx]; + for (int j=0; j<3; ++j) + m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); + + facet_visited[facet_idx] = true; + for (int j = 0; j < 3; ++ j) + if (int neighbor_idx = face_neighbors[facet_idx][j]; neighbor_idx >= 0 && ! facet_visited[neighbor_idx]) + facet_queue[facet_queue_cnt ++] = neighbor_idx; + } + } + m_planes.back().normal = normal_ptr->cast(); - for (size_t i = 0; i < ch.its.indices.size(); ++i) { - const Vec3i & face = ch.its.indices[i]; - m_planes.emplace_back(); - for (int j = 0; j < 3; ++j) - m_planes.back().vertices.emplace_back(ch.its.vertices[face[j]].cast()); - m_planes.back().normal = face_normals[i].cast(); Pointf3s& verts = m_planes.back().vertices; // Now we'll transform all the points into world coordinates, so that the areas, angles and distances // make real sense. verts = transform(verts, inst_matrix); + + // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway): + if (verts.size() == 3 && + ((verts[0] - verts[1]).norm() < minimal_side + || (verts[0] - verts[2]).norm() < minimal_side + || (verts[1] - verts[2]).norm() < minimal_side)) + m_planes.pop_back(); } // Let's prepare transformation of the normal vector from mesh to instance coordinates. @@ -445,12 +353,84 @@ void GLGizmoFlatten::update_planes() polygon = Slic3r::Geometry::convex_hull(polygon); polygon = transform(polygon, tr.inverse()); + // Calculate area of the polygons and discard ones that are too small + float& area = m_planes[polygon_id].area; + area = 0.f; + for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula + area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1); + area = 0.5f * std::abs(area); + + bool discard = false; + if (area < minimal_area) + discard = true; + else { + // We also check the inner angles and discard polygons with angles smaller than the following threshold + const double angle_threshold = ::cos(10.0 * (double)PI / 180.0); + + for (unsigned int i = 0; i < polygon.size(); ++i) { + const Vec3d& prec = polygon[(i == 0) ? polygon.size() - 1 : i - 1]; + const Vec3d& curr = polygon[i]; + const Vec3d& next = polygon[(i == polygon.size() - 1) ? 0 : i + 1]; + + if ((prec - curr).normalized().dot((next - curr).normalized()) > angle_threshold) { + discard = true; + break; + } + } + } + + if (discard) { + m_planes[polygon_id--] = std::move(m_planes.back()); + m_planes.pop_back(); + continue; + } + // We will shrink the polygon a little bit so it does not touch the object edges: Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); centroid /= (double)polygon.size(); for (auto& vertex : polygon) vertex = 0.9f*vertex + 0.1f*centroid; + // Polygon is now simple and convex, we'll round the corners to make them look nicer. + // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex + // towards their average (controlled by 'aggressivity'). This is repeated k times. + // In next iterations, the neighbours are not always taken at the middle (to increase the + // rounding effect at the corners, where we need it most). + const unsigned int k = 10; // number of iterations + const float aggressivity = 0.2f; // agressivity + const unsigned int N = polygon.size(); + std::vector> neighbours; + if (k != 0) { + Pointf3s points_out(2*k*N); // vector long enough to store the future vertices + for (unsigned int j=0; j Date: Wed, 12 Oct 2022 12:27:04 +0200 Subject: [PATCH 289/327] Measuring - Gizmo measure - Fixed update of volume matrix --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index db33135dda..6f8743e15b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -678,11 +678,11 @@ void GLGizmoMeasure::update_if_needed() } m_old_model_object = object; m_old_model_volume = volume; - - m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - m_volume_matrix_scale_inverse = Geometry::Transformation(m_volume_matrix).get_scaling_factor_matrix().inverse(); }; + m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + m_volume_matrix_scale_inverse = Geometry::Transformation(m_volume_matrix).get_scaling_factor_matrix().inverse(); + const ModelObject* mo = m_c->selection_info()->model_object(); const ModelVolume* mv = m_c->selection_info()->model_volume(); if (m_state != On || (mo == nullptr && mv == nullptr)) From cf11101d16ef82d1795fdc1cf1dee1211e108943 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 12 Oct 2022 13:38:53 +0200 Subject: [PATCH 290/327] Measuring - Gizmo measure - Measuring data converted to world coordinates --- src/libslic3r/Measure.cpp | 20 +++ src/libslic3r/Measure.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 196 +++++++++++++---------- 3 files changed, 131 insertions(+), 89 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 27a4f8a90d..20ae7f4138 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -1043,6 +1043,26 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& return result; } +void DistAndPoints::transform(const Transform3d& trafo) { + from = trafo * from; + to = trafo * to; + dist = (to - from).norm(); +} + +void AngleAndEdges::transform(const Transform3d& trafo) { + const Vec3d old_e1 = e1.second - e1.first; + const Vec3d old_e2 = e2.second - e2.first; + center = trafo * center; + e1.first = trafo * e1.first; + e1.second = trafo * e1.second; + e2.first = trafo * e2.first; + e2.second = trafo * e2.second; + angle = std::acos(std::clamp(Measure::edge_direction(e1).dot(Measure::edge_direction(e2)), -1.0, 1.0)); + const Vec3d new_e1 = e1.second - e1.first; + const Vec3d new_e2 = e2.second - e2.first; + const double average_scale = 0.5 * (new_e1.norm() / old_e1.norm() + new_e2.norm() / old_e2.norm()); + radius = average_scale * radius; +} diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index f310d8f9fa..ff1757d7ae 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -120,6 +120,8 @@ struct DistAndPoints { double dist; Vec3d from; Vec3d to; + + void transform(const Transform3d& trafo); }; struct AngleAndEdges { @@ -132,6 +134,8 @@ struct AngleAndEdges { double radius; bool coplanar; + void transform(const Transform3d& trafo); + static const AngleAndEdges Dummy; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 6f8743e15b..5400a7a586 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -108,6 +108,81 @@ static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const return init_data; } +class TransformHelper +{ + struct Cache + { + std::array viewport; + Matrix4d ndc_to_ss_matrix; + Transform3d ndc_to_ss_matrix_inverse; + }; + + static Cache s_cache; + +public: + static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { + return world_matrix * model; + } + + 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 Vec3d clip_to_ndc(const Vec4d& clip) { + return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); + } + + 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 Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); + } + + static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { + return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); + } + + static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { + return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); + } + + 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 const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix; + } + + static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { + update(viewport); + return s_cache.ndc_to_ss_matrix_inverse; + } + +private: + static void update(const std::array& viewport) { + if (s_cache.viewport == viewport) + return; + + const double half_w = 0.5 * double(viewport[2]); + const double half_h = 0.5 * double(viewport[3]); + s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, + 0.0, half_h, 0.0, double(viewport[1]) + half_h, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0; + + s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); + s_cache.viewport = viewport; + } +}; + +TransformHelper::Cache TransformHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; + GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) { @@ -174,8 +249,18 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_1_ID, *m_sphere.mesh_raycaster)); } - if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) + if (m_selected_features != selected_features_old && m_selected_features.second.feature.has_value()) { m_measurement_result = Measure::get_measurement(*m_selected_features.first.feature, *m_selected_features.second.feature, m_measuring.get()); + // transform to world coordinates + if (m_measurement_result.angle.has_value()) + m_measurement_result.angle->transform(m_volume_matrix); + if (m_measurement_result.distance_infinite.has_value()) + m_measurement_result.distance_infinite->transform(m_volume_matrix); + if (m_measurement_result.distance_strict.has_value()) + m_measurement_result.distance_strict->transform(m_volume_matrix); + if (m_measurement_result.distance_xyz.has_value()) + m_measurement_result.distance_xyz = TransformHelper::model_to_world(*m_measurement_result.distance_xyz, m_volume_matrix); + } return true; } @@ -725,77 +810,6 @@ void GLGizmoMeasure::restore_scene_raycasters_state() } } -class DimensioningHelper -{ - struct Cache - { - std::array viewport; - Matrix4d ndc_to_ss_matrix; - Transform3d ndc_to_ss_matrix_inverse; - }; - - static Cache s_cache; - -public: - static Vec3d model_to_world(const Vec3d& model, const Transform3d& world_matrix) { - return world_matrix * model; - } - - 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 Vec3d clip_to_ndc(const Vec4d& clip) { - return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w(); - } - - 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 Vec4d model_to_clip(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { - return world_to_clip(model_to_world(model, world_matrix), projection_view_matrix); - } - - static Vec3d model_to_ndc(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix) { - return clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)); - } - - static Vec2d model_to_ss(const Vec3d& model, const Transform3d& world_matrix, const Matrix4d& projection_view_matrix, const std::array& viewport) { - return ndc_to_ss(clip_to_ndc(world_to_clip(model_to_world(model, world_matrix), projection_view_matrix)), viewport); - } - - static const Matrix4d& ndc_to_ss_matrix(const std::array& viewport) { - update(viewport); - return s_cache.ndc_to_ss_matrix; - } - - static const Transform3d ndc_to_ss_matrix_inverse(const std::array& viewport) { - update(viewport); - return s_cache.ndc_to_ss_matrix_inverse; - } - -private: - static void update(const std::array& viewport) { - if (s_cache.viewport == viewport) - return; - - const double half_w = 0.5 * double(viewport[2]); - const double half_h = 0.5 * double(viewport[3]); - s_cache.ndc_to_ss_matrix << half_w, 0.0, 0.0, double(viewport[0]) + half_w, - 0.0, half_h, 0.0, double(viewport[1]) + half_h, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0; - - s_cache.ndc_to_ss_matrix_inverse = s_cache.ndc_to_ss_matrix.inverse(); - s_cache.viewport = viewport; - } -}; - -DimensioningHelper::Cache DimensioningHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; - void GLGizmoMeasure::render_dimensioning() { static SelectedFeatures last_selected_features; @@ -816,8 +830,8 @@ void GLGizmoMeasure::render_dimensioning() const std::array& viewport = camera.get_viewport(); // screen coordinates - const Vec2d v1ss = DimensioningHelper::model_to_ss(v1, m_volume_matrix, projection_view_matrix, viewport); - const Vec2d v2ss = DimensioningHelper::model_to_ss(v2, m_volume_matrix, projection_view_matrix, viewport); + const Vec2d v1ss = TransformHelper::world_to_ss(v1, projection_view_matrix, viewport); + const Vec2d v2ss = TransformHelper::world_to_ss(v2, projection_view_matrix, viewport); if (v1ss.isApprox(v2ss)) return; @@ -835,7 +849,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d v1ss_3 = { v1ss.x(), v1ss.y(), 0.0 }; const Vec3d v2ss_3 = { v2ss.x(), v2ss.y(), 0.0 }; - const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + const Transform3d ss_to_ndc_matrix = TransformHelper::ndc_to_ss_matrix_inverse(viewport); // stem shader->set_uniform("view_model_matrix", overlap ? @@ -951,8 +965,12 @@ void GLGizmoMeasure::render_dimensioning() auto point_edge = [this, shader](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Point && f2.get_type() == Measure::SurfaceFeatureType::Edge); - const std::pair e = f2.get_edge(); - const Vec3d v_proj = m_measurement_result.distance_infinite->to; + std::pair e = f2.get_edge(); + // Transform to world coordinates + e.first = TransformHelper::model_to_world(e.first, m_volume_matrix); + e.second = TransformHelper::model_to_world(e.second, m_volume_matrix); + + const Vec3d v_proj = m_measurement_result.distance_infinite->to; const Vec3d e1e2 = e.second - e.first; const Vec3d v_proje1 = v_proj - e.first; @@ -962,11 +980,11 @@ void GLGizmoMeasure::render_dimensioning() const Camera& camera = wxGetApp().plater()->get_camera(); const Matrix4d projection_view_matrix = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); const std::array& viewport = camera.get_viewport(); - const Transform3d ss_to_ndc_matrix = DimensioningHelper::ndc_to_ss_matrix_inverse(viewport); + const Transform3d ss_to_ndc_matrix = TransformHelper::ndc_to_ss_matrix_inverse(viewport); - const Vec2d v_projss = DimensioningHelper::model_to_ss(v_proj, m_volume_matrix, projection_view_matrix, viewport); + const Vec2d v_projss = TransformHelper::world_to_ss(v_proj, projection_view_matrix, viewport); auto render_extension = [this, &v_projss, &projection_view_matrix, &viewport, &ss_to_ndc_matrix, shader](const Vec3d& p) { - const Vec2d pss = DimensioningHelper::model_to_ss(p, m_volume_matrix, projection_view_matrix, viewport); + const Vec2d pss = TransformHelper::world_to_ss(p, projection_view_matrix, viewport); if (!pss.isApprox(v_projss)) { const Vec2d pv_projss = v_projss - pss; const double pv_projss_len = pv_projss.norm(); @@ -1029,7 +1047,7 @@ void GLGizmoMeasure::render_dimensioning() // arc const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center)); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)); m_dimensioning.arc.render(); // arrows @@ -1039,7 +1057,7 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d direction_model = (endpoint_id == 1) ? -normal.cross(position_model - center).normalized() : normal.cross(position_model - center).normalized(); const auto qz = Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); const auto qx = Eigen::Quaternion::FromTwoVectors(qz * Vec3d::UnitX(), direction_model); - const Transform3d view_model_matrix = camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(position_model) * + const Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::translation_transform(position_model) * qx * qz * Geometry::scale_transform(camera.get_inv_zoom()); shader->set_uniform("view_model_matrix", view_model_matrix); m_dimensioning.triangle.render(); @@ -1055,8 +1073,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)* + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second))* Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.render(); } @@ -1065,19 +1083,19 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_volume_matrix * Geometry::translation_transform(center) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)* + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second))* Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.render(); } // label - // label model coordinates - const Vec3d label_position_model = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); + // label world coordinates + const Vec3d label_position_world = Geometry::translation_transform(center) * (draw_radius * (Eigen::Quaternion(Eigen::AngleAxisd(step * 0.5 * double(resolution), normal)) * e1_unit)); // label screen coordinates const std::array& viewport = camera.get_viewport(); - const Vec2d label_position_ss = DimensioningHelper::model_to_ss(label_position_model, m_volume_matrix, + const Vec2d label_position_ss = TransformHelper::world_to_ss(label_position_world, camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(), viewport); m_imgui->set_next_window_pos(label_position_ss.x(), viewport[3] - label_position_ss.y(), ImGuiCond_Always, 0.0f, 1.0f); From a62a167c1d53a66c2e8b273db1b7194a1a6baf10 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 14 Oct 2022 09:05:36 +0200 Subject: [PATCH 291/327] Measuring - Fixed crash when clicking on Delete All command while the Gizmo measure is active --- src/slic3r/GUI/Plater.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 326bd79e26..0fc8dbcd1a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3024,6 +3024,9 @@ void Plater::priv::delete_all_objects_from_model() gcode_result.reset(); view3D->get_canvas3d()->reset_sequential_print_clearance(); +#if ENABLE_MEASURE_GIZMO + view3D->get_canvas3d()->reset_all_gizmos(); +#endif // ENABLE_MEASURE_GIZMO m_worker.cancel_all(); From 844d30f64e5f1e7bffad0f2fbe805084fdf8e4b5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 14 Oct 2022 09:13:40 +0200 Subject: [PATCH 292/327] Measuring - Gizmo measure - Definition and rendering of point and edge features in world coordinates --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 24 ++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 1 - 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 5400a7a586..40b0c96aa0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -562,8 +562,8 @@ void GLGizmoMeasure::on_render() default: { assert(false); break; } case Measure::SurfaceFeatureType::Point: { - const Vec3d& position = feature.get_point(); - const Transform3d feature_matrix = m_volume_matrix * Geometry::translation_transform(position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Vec3d position = TransformHelper::model_to_world(feature.get_point(), m_volume_matrix); + const Transform3d feature_matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(feature_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -578,7 +578,8 @@ void GLGizmoMeasure::on_render() { const auto& [center, radius, normal] = feature.get_circle(); // render center - const Transform3d center_matrix = m_volume_matrix * Geometry::translation_transform(center) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Vec3d center_world = TransformHelper::model_to_world(center, m_volume_matrix); + const Transform3d center_matrix = Geometry::translation_transform(center_world) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(center_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -608,11 +609,12 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Edge: { - const auto& [start, end] = feature.get_edge(); + const auto& [from, to] = feature.get_edge(); // render extra point const std::optional extra = feature.get_extra_point(); if (extra.has_value()) { - const Transform3d point_matrix = m_volume_matrix * Geometry::translation_transform(*extra) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Vec3d extra_world = TransformHelper::model_to_world(*extra, m_volume_matrix); + const Transform3d point_matrix = Geometry::translation_transform(extra_world) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(point_matrix); m_sphere.model.set_color(colors.front()); m_sphere.model.render(); @@ -623,9 +625,11 @@ void GLGizmoMeasure::on_render() } } // render edge - const Transform3d edge_matrix = m_volume_matrix * Geometry::translation_transform(start) * - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), end - start) * - Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (end - start).norm() }); + const Vec3d from_world = TransformHelper::model_to_world(from, m_volume_matrix); + const Vec3d to_world = TransformHelper::model_to_world(to, m_volume_matrix); + const Transform3d edge_matrix = Geometry::translation_transform(from_world) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), to_world - from_world) * + Geometry::scale_transform({ (double)inv_zoom, (double)inv_zoom, (to_world - from_world).norm() }); set_matrix_uniforms(edge_matrix); m_cylinder.model.set_color(colors.back()); m_cylinder.model.render(); @@ -717,7 +721,8 @@ void GLGizmoMeasure::on_render() if (is_hovering_on_locked_feature && m_curr_point_on_feature_position.has_value()) { if (m_hover_id != POINT_ID) { - const Transform3d matrix = m_volume_matrix * Geometry::translation_transform(*m_curr_point_on_feature_position) * m_volume_matrix_scale_inverse * Geometry::scale_transform(inv_zoom); + const Vec3d position = TransformHelper::model_to_world(*m_curr_point_on_feature_position, m_volume_matrix); + const Transform3d matrix = Geometry::translation_transform(position) * Geometry::scale_transform(inv_zoom); set_matrix_uniforms(matrix); m_sphere.model.set_color(hover_selection_color()); m_sphere.model.render(); @@ -766,7 +771,6 @@ void GLGizmoMeasure::update_if_needed() }; m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); - m_volume_matrix_scale_inverse = Geometry::Transformation(m_volume_matrix).get_scaling_factor_matrix().inverse(); const ModelObject* mo = m_c->selection_info()->model_object(); const ModelVolume* mv = m_c->selection_info()->model_volume(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index fd93ba4aac..9db2a73aa3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -87,7 +87,6 @@ class GLGizmoMeasure : public GLGizmoBase Dimensioning m_dimensioning; Transform3d m_volume_matrix{ Transform3d::Identity() }; - Transform3d m_volume_matrix_scale_inverse{ Transform3d::Identity() }; std::vector m_plane_models_cache; std::map> m_raycasters; std::vector> m_selection_raycasters; From 64c57faf8faf0a5b9375f75f36db07a5834ebb30 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Oct 2022 10:46:33 +0200 Subject: [PATCH 293/327] 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 597152c06a..54d4fcab74 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 0d54ebc1f7..ee611b0066 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 f907b03e1e..6be19c88f8 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 cced147f8e..663290a8be 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 3dd02ab8dc..eb718b48d4 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 d751325424..459c6390c9 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 7c7fbc9898..2051192ee4 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 294/327] 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 54d4fcab74..56f3af2d1d 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 5164c691e2..ff9113c684 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 663290a8be..d9cc1b1cac 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 295/327] 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 459c6390c9..9965e48460 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 21d7c64f8a..86e14705b7 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 296/327] 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 56f3af2d1d..aa21c3adeb 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 2b58758158..c83e3f9703 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 ff9113c684..6ffe19218d 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 d9cc1b1cac..ac96029855 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 9965e48460..793ab1e512 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 2051192ee4..15ed60a99a 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 83db044f0490708ae651ec175893d2b97c0ac6b2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Oct 2022 08:17:53 +0200 Subject: [PATCH 297/327] Measuring - Gizmo measure - Definition and rendering of circle features in world coordinates --- src/slic3r/GUI/GLModel.cpp | 44 +++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 105 +++++++++++++++++++---- 2 files changed, 108 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 70af07b7d7..2d4d25f8c8 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2348,37 +2348,37 @@ GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float h GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness) { - primary_resolution = std::max(4, primary_resolution); - secondary_resolution = std::max(4, secondary_resolution); - const unsigned int torusSectorCount = primary_resolution; - const float torusSectorStep = 2.0f * float(M_PI) / float(torusSectorCount); - const unsigned int sectionSectorCount = secondary_resolution; - const float sectionSectorStep = 2.0f * float(M_PI) / float(sectionSectorCount); + const unsigned int torus_sector_count = std::max(4, primary_resolution); + const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count); + const unsigned int section_sector_count = std::max(4, secondary_resolution); + const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count); GLModel::Geometry data; data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; - data.reserve_vertices(torusSectorCount * sectionSectorCount); - data.reserve_indices(torusSectorCount * sectionSectorCount * 2 * 3); + data.reserve_vertices(torus_sector_count * section_sector_count); + data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3); // vertices - for (unsigned int i = 0; i < torusSectorCount; ++i) { - const float sectionAngle = torusSectorStep * i; - const Vec3f sectionCenter(radius * std::cos(sectionAngle), radius * std::sin(sectionAngle), 0.0f); - for (unsigned int j = 0; j < sectionSectorCount; ++j) { - const float circleAngle = sectionSectorStep * j; - const float thickness_xy = thickness * std::cos(circleAngle); - const float thickness_z = thickness * std::sin(circleAngle); - const Vec3f v(thickness_xy * std::cos(sectionAngle), thickness_xy * std::sin(sectionAngle), thickness_z); - data.add_vertex(sectionCenter + v, (Vec3f)v.normalized()); + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const float section_angle = torus_sector_step * i; + const float csa = std::cos(section_angle); + const float ssa = std::sin(section_angle); + const Vec3f section_center(radius * csa, radius * ssa, 0.0f); + for (unsigned int j = 0; j < section_sector_count; ++j) { + const float circle_angle = section_sector_step * j; + const float thickness_xy = thickness * std::cos(circle_angle); + const float thickness_z = thickness * std::sin(circle_angle); + const Vec3f v(thickness_xy * csa, thickness_xy * ssa, thickness_z); + data.add_vertex(section_center + v, (Vec3f)v.normalized()); } } // triangles - for (unsigned int i = 0; i < torusSectorCount; ++i) { - const unsigned int ii = i * sectionSectorCount; - const unsigned int ii_next = ((i + 1) % torusSectorCount) * sectionSectorCount; - for (unsigned int j = 0; j < sectionSectorCount; ++j) { - const unsigned int j_next = (j + 1) % sectionSectorCount; + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const unsigned int ii = i * section_sector_count; + const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const unsigned int j_next = (j + 1) % section_sector_count; const unsigned int i0 = ii + j; const unsigned int i1 = ii_next + j; const unsigned int i2 = ii_next + j_next; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 40b0c96aa0..fdd94120a4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -108,6 +108,54 @@ static GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const return init_data; } +static GLModel::Geometry init_torus_data(unsigned int primary_resolution, unsigned int secondary_resolution, const Vec3f& center, + float radius, float thickness, const Vec3f& model_axis, const Transform3f& world_trafo) +{ + const unsigned int torus_sector_count = std::max(4, primary_resolution); + const unsigned int section_sector_count = std::max(4, secondary_resolution); + const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count); + const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count); + + GLModel::Geometry data; + data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 }; + data.reserve_vertices(torus_sector_count * section_sector_count); + data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3); + + // vertices + const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast()).cast() * + Eigen::Quaternion::FromTwoVectors(Vec3f::UnitZ(), model_axis); + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const float section_angle = torus_sector_step * i; + const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f); + const Vec3f local_section_center = radius * radius_dir; + const Vec3f world_section_center = local_to_world_matrix * local_section_center; + const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized(); + const Vec3f world_section_normal = (Vec3f)(local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized(); + const Vec3f base_v = thickness * radius_dir; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v; + data.add_vertex(world_section_center + v, (Vec3f)v.normalized()); + } + } + + // triangles + for (unsigned int i = 0; i < torus_sector_count; ++i) { + const unsigned int ii = i * section_sector_count; + const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count; + for (unsigned int j = 0; j < section_sector_count; ++j) { + const unsigned int j_next = (j + 1) % section_sector_count; + const unsigned int i0 = ii + j; + const unsigned int i1 = ii_next + j; + const unsigned int i2 = ii_next + j_next; + const unsigned int i3 = ii + j_next; + data.add_triangle(i0, i1, i2); + data.add_triangle(i0, i2, i3); + } + } + + return data; +} + class TransformHelper { struct Cache @@ -411,6 +459,20 @@ void GLGizmoMeasure::on_render() const bool mouse_on_object = m_c->raycaster()->raycasters().front()->unproject_on_mesh(m_mouse_pos, m_volume_matrix, camera, position_on_model, normal_on_model, nullptr, &model_facet_idx); const bool is_hovering_on_locked_feature = m_mode == EMode::ExtendedSelection && m_hover_id != -1; + auto update_circle = [this, inv_zoom]() { + if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { + m_last_inv_zoom = inv_zoom; + m_last_circle = m_curr_feature; + m_circle.reset(); + const auto [center, radius, normal] = m_curr_feature->get_circle(); + GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), m_volume_matrix.cast()); + m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); + m_circle.model.init_from(std::move(circle_geometry)); + return true; + } + return false; + }; + if (m_mode == EMode::BasicSelection) { std::optional curr_feature = mouse_on_object ? m_measuring->get_feature(model_facet_idx, position_on_model.cast()) : std::nullopt; m_curr_point_on_feature_position.reset(); @@ -441,16 +503,7 @@ void GLGizmoMeasure::on_render() } case Measure::SurfaceFeatureType::Circle: { - const auto [center, radius, normal] = m_curr_feature->get_circle(); - if (m_last_inv_zoom != inv_zoom || m_last_circle != m_curr_feature) { - m_last_inv_zoom = inv_zoom; - m_last_circle = m_curr_feature; - m_circle.reset(); - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); - m_circle.mesh_raycaster = std::make_unique(std::make_shared(circle_geometry.get_as_indexed_triangle_set())); - m_circle.model.init_from(std::move(circle_geometry)); - } - + update_circle(); m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); m_raycasters.insert({ POINT_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, POINT_ID, *m_sphere.mesh_raycaster) }); break; @@ -519,18 +572,32 @@ void GLGizmoMeasure::on_render() if (m_hover_id == POINT_ID) m_curr_point_on_feature_position = center; else { - const float r = radius; // needed for the following lambda - m_curr_point_on_feature_position = m_volume_matrix.inverse() * position_on_feature(CIRCLE_ID, camera, [r](const Vec3f& v) { - float angle = std::atan2(v.y(), v.x()); - if (angle < 0.0f) - angle += 2.0f * float(M_PI); - return Vec3f(float(r) * std::cos(angle), float(r) * std::sin(angle), 0.0f); - }); + const Vec3d world_pof = position_on_feature(CIRCLE_ID, camera, [](const Vec3f& v) { return v; }); + const Eigen::Hyperplane plane(m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()* normal, m_volume_matrix * center); + const Transform3d local_to_model_matrix = Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Vec3d local_proj = local_to_model_matrix.inverse() * m_volume_matrix.inverse() * plane.projection(world_pof); + double angle = std::atan2(local_proj.y(), local_proj.x()); + if (angle < 0.0) + angle += 2.0 * double(M_PI); + + const Vec3d local_pos = radius * Vec3d(std::cos(angle), std::sin(angle), 0.0); + m_curr_point_on_feature_position = local_to_model_matrix * local_pos; } break; } } } + else { + if (m_curr_feature.has_value() && m_curr_feature->get_type() == Measure::SurfaceFeatureType::Circle) { + if (update_circle()) { + m_parent.remove_raycasters_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID); + auto it = m_raycasters.find(CIRCLE_ID); + if (it != m_raycasters.end()) + m_raycasters.erase(it); + m_raycasters.insert({ CIRCLE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, CIRCLE_ID, *m_circle.mesh_raycaster) }); + } + } + } if (!m_curr_feature.has_value() && !m_selected_features.first.feature.has_value()) return; @@ -589,7 +656,7 @@ void GLGizmoMeasure::on_render() it->second->set_transform(center_matrix); } // render circle - const Transform3d circle_matrix = m_volume_matrix * Geometry::translation_transform(center) * Eigen::Quaternion::FromTwoVectors(Vec3d::UnitZ(), normal); + const Transform3d circle_matrix = Transform3d::Identity(); set_matrix_uniforms(circle_matrix); if (update_raycasters_transform) { m_circle.model.set_color(colors.back()); @@ -600,7 +667,7 @@ void GLGizmoMeasure::on_render() } else { GLModel circle; - GLModel::Geometry circle_geometry = smooth_torus(64, 16, float(radius), 5.0f * inv_zoom); + GLModel::Geometry circle_geometry = init_torus_data(64, 16, center.cast(), float(radius), 5.0f * inv_zoom, normal.cast(), m_volume_matrix.cast()); circle.init_from(std::move(circle_geometry)); circle.set_color(colors.back()); circle.render(); From 03f2f1478af2aa24a272cc1a2cc2dec9762f58b5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Oct 2022 08:38:50 +0200 Subject: [PATCH 298/327] Removed tech DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES --- src/libslic3r/Technologies.hpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 817136e7a4..04ecfae670 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -68,7 +68,6 @@ #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) // Enable Measure Gizmo #define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) -#define DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES (1 && ENABLE_MEASURE_GIZMO) #define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index fdd94120a4..2eff5bb2ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -425,12 +425,9 @@ bool GLGizmoMeasure::on_is_activable() const bool res = (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) ? selection.is_single_full_instance() : selection.is_single_volume() || selection.is_single_volume_instance(); - if (res) { + if (res) res &= !selection.get_first_volume()->is_sinking(); -#if DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES - res &= Geometry::Transformation(selection.get_first_volume()->world_matrix()).get_scaling_factor().isApprox(Vec3d::Ones()); -#endif // DISABLE_MEASURE_GIZMO_FOR_SCALED_VOLUMES - } + return res; } From 2880704de9060ace26b695b851af60682acc5fb6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Oct 2022 17:33:58 +0200 Subject: [PATCH 299/327] 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 6be19c88f8..4f34572d2e 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 793ab1e512..56dd564627 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 86e14705b7..941d016600 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 e2fca5faed..50577771bc 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 55dbaafe2e..6f2d1519c5 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 15ed60a99a..d3bd704d21 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 300/327] 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 aa21c3adeb..3c4e004544 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 e674b52d02..bedb3718ef 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 ac96029855..5be1efed12 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 56dd564627..fcb5cc2473 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 57aa5da983..40f9817227 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 69f8084516..a0b53dadf9 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 301/327] 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 b6f95eead5..d8a9474b31 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 a331c9d018f82543b0e979145debe5e6e830e188 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Oct 2022 12:57:29 +0200 Subject: [PATCH 302/327] Measuring - Gizmo measure - Fixed measurements for circles for scaled volumes --- src/libslic3r/MeasureUtils.hpp | 10 +++++----- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp index 4f84624eac..34fea9855c 100644 --- a/src/libslic3r/MeasureUtils.hpp +++ b/src/libslic3r/MeasureUtils.hpp @@ -93,7 +93,7 @@ protected: std::vector m_coefficient; }; -Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) +inline Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) { const uint32_t p0Degree = p0.GetDegree(); const uint32_t p1Degree = p1.GetDegree(); @@ -107,7 +107,7 @@ Polynomial1 operator * (const Polynomial1& p0, const Polynomial1& p1) return result; } -Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) +inline Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) { const uint32_t p0Degree = p0.GetDegree(); const uint32_t p1Degree = p1.GetDegree(); @@ -136,7 +136,7 @@ Polynomial1 operator + (const Polynomial1& p0, const Polynomial1& p1) } } -Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) +inline Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) { const uint32_t p0Degree = p0.GetDegree(); const uint32_t p1Degree = p1.GetDegree(); @@ -165,7 +165,7 @@ Polynomial1 operator - (const Polynomial1& p0, const Polynomial1& p1) } } -Polynomial1 operator * (double scalar, const Polynomial1& p) +inline Polynomial1 operator * (double scalar, const Polynomial1& p) { const uint32_t degree = p.GetDegree(); Polynomial1 result(degree); @@ -354,7 +354,7 @@ public: // the maximum absolute component occurs at index i, then the orthogonal // vector U has u[i] = v[i+1], u[i+1] = -v[i], and all other components // zero. The index addition i+1 is computed modulo N. -Vec3d get_orthogonal(const Vec3d& v, bool unitLength) +inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength) { double cmax = std::fabs(v[0]); int32_t imax = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 2eff5bb2ad..14caa193de 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -9,6 +9,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/MeasureUtils.hpp" #include @@ -1486,8 +1487,11 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit case Measure::SurfaceFeatureType::Circle: { auto [center, radius, normal] = m_curr_feature->get_circle(); + // generic point on circle, used to recalculate radius after transformation + const Vec3d on_circle = m_volume_matrix * (center + radius * Measure::get_orthogonal(normal, true)); center = m_volume_matrix * center; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + normal = (m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal).normalized(); + radius = (on_circle - center).norm(); if (use_inches) { center = ObjectManipulation::mm_to_in * center; radius = ObjectManipulation::mm_to_in * radius; From 121b0f9a604503b777f6b2de1dc59fb65ea3333a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Oct 2022 14:07:56 +0200 Subject: [PATCH 303/327] Measuring - Gizmo measure - Fixed crash when creating a new project using CTRL+N while the gizmo is active --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 14caa193de..86fa9ff5c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -835,7 +835,11 @@ void GLGizmoMeasure::update_if_needed() m_old_model_volume = volume; }; - m_volume_matrix = m_parent.get_selection().get_first_volume()->world_matrix(); + const Selection& selection = m_parent.get_selection(); + if (selection.is_empty()) + return; + + m_volume_matrix = selection.get_first_volume()->world_matrix(); const ModelObject* mo = m_c->selection_info()->model_object(); const ModelVolume* mv = m_c->selection_info()->model_volume(); From ca923c084fa02389e397106478c909dce1526d3c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Oct 2022 15:13:56 +0200 Subject: [PATCH 304/327] Measuring - Gizmo measure - Fixed measure of distance point-circle when the point is the circle's center --- src/libslic3r/Measure.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 20ae7f4138..c060ae8938 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -632,13 +632,21 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& } else if (f2.get_type() == SurfaceFeatureType::Circle) { // Find a plane containing normal, center and the point. const auto [c, radius, n] = f2.get_circle(); - Eigen::Hyperplane circle_plane(n, c); - Vec3d proj = circle_plane.projection(f1.get_point()); - double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + - (f1.get_point() - proj).squaredNorm()); + const Eigen::Hyperplane circle_plane(n, c); + const Vec3d proj = circle_plane.projection(f1.get_point()); + if (proj.isApprox(c)) { + const Vec3d p_on_circle = c + radius * get_orthogonal(n, true); + result.distance_strict = std::make_optional(DistAndPoints{ radius, c, p_on_circle }); + } + else { + const Eigen::Hyperplane circle_plane(n, c); + const Vec3d proj = circle_plane.projection(f1.get_point()); + const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + + (f1.get_point() - proj).squaredNorm()); - const Vec3d p_on_circle = c + radius * (circle_plane.projection(f1.get_point()) - c).normalized(); - result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle}); // TODO + const Vec3d p_on_circle = c + radius * (proj - c).normalized(); + result.distance_strict = std::make_optional(DistAndPoints{ dist, f1.get_point(), p_on_circle }); // TODO + } /////////////////////////////////////////////////////////////////////////// } else if (f2.get_type() == SurfaceFeatureType::Plane) { const auto [idx, normal, pt] = f2.get_plane(); From 98d7fe335b7cd912dfa12b1374b15a82193c548e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 21 Oct 2022 16:07:41 +0200 Subject: [PATCH 305/327] 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 fcb5cc2473..d8973228a6 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 941d016600..95cd10fa83 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 306/327] 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 2285c29a68..054ccd4ea3 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 d8973228a6..7695a902d6 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 95cd10fa83..e33c5c897c 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 a08836c28b..435d8dc6e4 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 7c41a64b9d..79483e4033 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 d8a9474b31..f711ecf03c 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 726f228caa..186b74febc 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 59f1c349fcf580b57c3e1a2d42d149b40746a5b5 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Oct 2022 08:25:31 +0200 Subject: [PATCH 307/327] Measuring - Gizmo measure - Show dimensioning while the user pans/rotates the 3D view --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 86fa9ff5c3..0e9b33b26b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -438,9 +438,9 @@ void GLGizmoMeasure::on_render() render_debug_dialog(); #endif // ENABLE_MEASURE_GIZMO_DEBUG - // do not render if the user is panning/rotating the 3d scene - if (m_parent.is_mouse_dragging()) - return; +// // do not render if the user is panning/rotating the 3d scene +// if (m_parent.is_mouse_dragging()) +// return; const Selection& selection = m_parent.get_selection(); @@ -610,6 +610,8 @@ void GLGizmoMeasure::on_render() glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); + const bool old_cullface = ::glIsEnabled(GL_CULL_FACE); + glsafe(::glDisable(GL_CULL_FACE)); const Transform3d& view_matrix = camera.get_view_matrix(); @@ -795,6 +797,9 @@ void GLGizmoMeasure::on_render() } shader->stop_using(); + + if (old_cullface) + glsafe(::glEnable(GL_CULL_FACE)); } render_dimensioning(); From 5561e22ba7499a7246d18f481f7c3e10097bf0be Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Oct 2022 09:25:05 +0200 Subject: [PATCH 308/327] Measuring - Gizmo measure - Render dimensioning auxiliary lines in light gray color --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0e9b33b26b..b89b6785d0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -933,7 +933,8 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("view_model_matrix", overlap ? ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::translation_transform(-2.0 * TRIANGLE_HEIGHT * Vec3d::UnitX()) * Geometry::scale_transform({ v12ss_len + 4.0 * TRIANGLE_HEIGHT, 1.0f, 1.0f }) : ss_to_ndc_matrix * Geometry::translation_transform(v1ss_3) * q12ss * Geometry::scale_transform({ v12ss_len, 1.0f, 1.0f })); - m_dimensioning.line.render(); + m_dimensioning.line.set_color(ColorRGBA::WHITE()); + m_dimensioning.line.render(); // arrow 1 shader->set_uniform("view_model_matrix", overlap ? @@ -1072,6 +1073,7 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("projection_matrix", Transform3d::Identity()); shader->set_uniform("view_model_matrix", ss_to_ndc_matrix * Geometry::translation_transform({ pss.x(), pss.y(), 0.0 }) * q * Geometry::scale_transform({ pv_projss_len, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); m_dimensioning.line.render(); } }; @@ -1154,6 +1156,7 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)* Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second))* Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); m_dimensioning.line.render(); } @@ -1164,6 +1167,7 @@ void GLGizmoMeasure::render_dimensioning() shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)* Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second))* Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); + m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); m_dimensioning.line.render(); } From 402fe908c16d5cdf9fbf2356b98e1ce95a6cf262 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Oct 2022 10:03:35 +0200 Subject: [PATCH 309/327] Measuring - Gizmo measure - Fixed crash when selecting two parallel planes --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index b89b6785d0..172992db93 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1208,6 +1208,9 @@ void GLGizmoMeasure::render_dimensioning() auto arc_plane_plane = [this, arc_edge_edge](const Measure::SurfaceFeature& f1, const Measure::SurfaceFeature& f2) { assert(f1.get_type() == Measure::SurfaceFeatureType::Plane && f2.get_type() == Measure::SurfaceFeatureType::Plane); + if (!m_measurement_result.angle.has_value()) + return; + const std::pair e1 = m_measurement_result.angle->e1; const std::pair e2 = m_measurement_result.angle->e2; const double calc_radius = m_measurement_result.angle->radius; From 2f130ca6e1f8c4c12c9f5f2175bdb1423015c227 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Oct 2022 12:12:33 +0200 Subject: [PATCH 310/327] Measuring - Gizmo measure - Do not close the gizmo when the user clicks outside of any volume --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 172992db93..1fc063822e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -325,6 +325,9 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_mouse_left_down = false; return true; } + if (m_hover_id == -1 && !m_parent.is_mouse_dragging()) + // avoid closing the gizmo if the user clicks outside of any volume + return true; } else if (mouse_event.RightDown() && mouse_event.CmdDown()) { m_selected_features.reset(); @@ -1153,8 +1156,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e11center = center - e1.first; const double e11center_len = e11center.norm(); if (e11center_len > EPSILON && e11center.dot(e11e12) < 0.0) { - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)* - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second))* + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e1.first, e1.second)) * Geometry::scale_transform({ e11center_len, 1.0f, 1.0f })); m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); m_dimensioning.line.render(); @@ -1164,8 +1167,8 @@ void GLGizmoMeasure::render_dimensioning() const Vec3d e21center = center - e2.first; const double e21center_len = e21center.norm(); if (e21center_len > EPSILON) { - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center)* - Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second))* + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * Geometry::translation_transform(center) * + Eigen::Quaternion::FromTwoVectors(Vec3d::UnitX(), Measure::edge_direction(e2.first, e2.second)) * Geometry::scale_transform({ (coplanar && radius > 0.0) ? e21center_len : draw_radius, 1.0f, 1.0f })); m_dimensioning.line.set_color(ColorRGBA::LIGHT_GRAY()); m_dimensioning.line.render(); From a0c1648f365284c4bd2bf4fc1e76ad825fb6bad0 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Oct 2022 14:09:27 +0200 Subject: [PATCH 311/327] Measuring - Gizmo measure - Allow to unselect the first feature by clicking on it (also reworked the imgui dialog to show the action taken by left click in dependence of mouse position) --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 41 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 1fc063822e..d5ad0e4090 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -290,6 +290,10 @@ bool GLGizmoMeasure::on_mouse(const wxMouseEvent &mouse_event) m_selection_raycasters.push_back(m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, SELECTION_2_ID, *m_sphere.mesh_raycaster)); } } + else { + if (!m_selected_features.second.feature.has_value()) + m_selected_features.first.reset(); + } } else { const SelectedFeatures::Item item = item_from_feature(); @@ -1398,7 +1402,7 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit if (m_editing_distance) return; - m_imgui->begin(_u8L("Measure tool"), 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 const float win_h = ImGui::GetWindowHeight(); @@ -1417,18 +1421,39 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit unsigned int row_count = 1; add_row_to_table( [this]() { - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); + m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Left mouse button")); }, [this]() { - m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), - m_selected_features.second.feature.has_value() ? - ((m_mode == EMode::BasicSelection) ? _u8L("Select/Unselect feature") : _u8L("Select/Unselect point")) : - ((m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"))); + std::string text; + ColorRGBA color; + if (m_selected_features.second.feature.has_value()) { + if (m_selected_features.second.feature == m_curr_feature && m_mode == EMode::BasicSelection) + text = _u8L("Unselect feature"); + else if (m_hover_id == SELECTION_2_ID) + text = _u8L("Unselect point"); + else + text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"); + color = SELECTED_2ND_COLOR; + } + else { + if (m_selected_features.first.feature.has_value()) { + if (m_selected_features.first.feature == m_curr_feature) + text = _u8L("Unselect feature"); + else if (m_hover_id == SELECTION_1_ID) + text = _u8L("Unselect point"); + color = SELECTED_1ST_COLOR; + } + if (text.empty()) { + text = (m_mode == EMode::BasicSelection) ? _u8L("Select feature") : _u8L("Select point"); + color = m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR; + } + } + + m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), text); ImGui::SameLine(); const ImVec2 pos = ImGui::GetCursorScreenPos(); const float rect_size = ImGui::GetTextLineHeight(); - ImGui::GetWindowDrawList()->AddRectFilled({ pos.x + 1.0f, pos.y + 1.0f }, { pos.x + rect_size, pos.y + rect_size }, - ImGuiWrapper::to_ImU32(m_selected_features.first.feature.has_value() ? SELECTED_2ND_COLOR : SELECTED_1ST_COLOR)); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + rect_size, pos.y + rect_size), ImGuiWrapper::to_ImU32(color)); ImGui::Dummy(ImVec2(rect_size, rect_size)); } ); From 377ff4a5194a4c633abcc072ed82377148e24c89 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Oct 2022 14:50:54 +0200 Subject: [PATCH 312/327] Measuring - Gizmo measure - Commented out hovered feature section from imgui dialog --- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 190 +++++++++++------------ 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index d5ad0e4090..58e23b695f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -1479,103 +1479,103 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit const bool use_inches = wxGetApp().app_config->get("use_inches") == "1"; const std::string units = use_inches ? " " + _u8L("in") : " " + _u8L("mm"); - const Measure::SurfaceFeatureType feature_type = m_curr_feature.has_value() ? m_curr_feature->get_type() : Measure::SurfaceFeatureType::Undef; - bool data_text_set = false; - ImGui::Separator(); - if (feature_type != Measure::SurfaceFeatureType::Undef) { - if (m_mode == EMode::BasicSelection) { - m_imgui->text(surface_feature_type_as_string(feature_type)); - data_text_set = true; - } - else if (m_mode == EMode::ExtendedSelection) { - if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { - m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); - data_text_set = true; - } - } - } - if (!data_text_set) - m_imgui->text(_u8L("No feature")); + //const Measure::SurfaceFeatureType feature_type = m_curr_feature.has_value() ? m_curr_feature->get_type() : Measure::SurfaceFeatureType::Undef; + //bool data_text_set = false; + //ImGui::Separator(); + //if (feature_type != Measure::SurfaceFeatureType::Undef) { + // if (m_mode == EMode::BasicSelection) { + // m_imgui->text(surface_feature_type_as_string(feature_type)); + // data_text_set = true; + // } + // else if (m_mode == EMode::ExtendedSelection) { + // if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { + // m_imgui->text(point_on_feature_type_as_string(feature_type, m_hover_id)); + // data_text_set = true; + // } + // } + //} + //if (!data_text_set) + // m_imgui->text(_u8L("No feature")); - const unsigned int max_data_row_count = 3; - unsigned int data_row_count = 0; - if (ImGui::BeginTable("Data", 2)) { - if (m_mode == EMode::BasicSelection) { - switch (feature_type) - { - default: { break; } - case Measure::SurfaceFeatureType::Point: - { - Vec3d position = m_volume_matrix * m_curr_feature->get_point(); - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - data_row_count = 1; - break; - } - case Measure::SurfaceFeatureType::Edge: - { - auto [from, to] = m_curr_feature->get_edge(); - from = m_volume_matrix * from; - to = m_volume_matrix * to; - if (use_inches) { - from = ObjectManipulation::mm_to_in * from; - to = ObjectManipulation::mm_to_in * to; - } - add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - data_row_count = 3; - break; - } - case Measure::SurfaceFeatureType::Circle: - { - auto [center, radius, normal] = m_curr_feature->get_circle(); - // generic point on circle, used to recalculate radius after transformation - const Vec3d on_circle = m_volume_matrix * (center + radius * Measure::get_orthogonal(normal, true)); - center = m_volume_matrix * center; - normal = (m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal).normalized(); - radius = (on_circle - center).norm(); - if (use_inches) { - center = ObjectManipulation::mm_to_in * center; - radius = ObjectManipulation::mm_to_in * radius; - } - add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - data_row_count = 3; - break; - } - case Measure::SurfaceFeatureType::Plane: - { - auto [idx, normal, origin] = m_curr_feature->get_plane(); - origin = m_volume_matrix * origin; - normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; - if (use_inches) - origin = ObjectManipulation::mm_to_in * origin; - add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - data_row_count = 2; - break; - } - } - } - else { - if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { - Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; - if (use_inches) - position = ObjectManipulation::mm_to_in * position; - add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); - data_row_count = 1; - } - } + //const unsigned int max_data_row_count = 3; + //unsigned int data_row_count = 0; + //if (ImGui::BeginTable("Data", 2)) { + // if (m_mode == EMode::BasicSelection) { + // switch (feature_type) + // { + // default: { break; } + // case Measure::SurfaceFeatureType::Point: + // { + // Vec3d position = m_volume_matrix * m_curr_feature->get_point(); + // if (use_inches) + // position = ObjectManipulation::mm_to_in * position; + // add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // data_row_count = 1; + // break; + // } + // case Measure::SurfaceFeatureType::Edge: + // { + // auto [from, to] = m_curr_feature->get_edge(); + // from = m_volume_matrix * from; + // to = m_volume_matrix * to; + // if (use_inches) { + // from = ObjectManipulation::mm_to_in * from; + // to = ObjectManipulation::mm_to_in * to; + // } + // add_strings_row_to_table(*m_imgui, _u8L("From"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(from), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // add_strings_row_to_table(*m_imgui, _u8L("To"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(to), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // add_strings_row_to_table(*m_imgui, _u8L("Length"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double((to - from).norm()) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // data_row_count = 3; + // break; + // } + // case Measure::SurfaceFeatureType::Circle: + // { + // auto [center, radius, normal] = m_curr_feature->get_circle(); + // // generic point on circle, used to recalculate radius after transformation + // const Vec3d on_circle = m_volume_matrix * (center + radius * Measure::get_orthogonal(normal, true)); + // center = m_volume_matrix * center; + // normal = (m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal).normalized(); + // radius = (on_circle - center).norm(); + // if (use_inches) { + // center = ObjectManipulation::mm_to_in * center; + // radius = ObjectManipulation::mm_to_in * radius; + // } + // add_strings_row_to_table(*m_imgui, _u8L("Center"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(center), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // add_strings_row_to_table(*m_imgui, _u8L("Radius"), ImGuiWrapper::COL_ORANGE_LIGHT, format_double(radius) + units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // data_row_count = 3; + // break; + // } + // case Measure::SurfaceFeatureType::Plane: + // { + // auto [idx, normal, origin] = m_curr_feature->get_plane(); + // origin = m_volume_matrix * origin; + // normal = m_volume_matrix.matrix().block(0, 0, 3, 3).inverse().transpose() * normal; + // if (use_inches) + // origin = ObjectManipulation::mm_to_in * origin; + // add_strings_row_to_table(*m_imgui, _u8L("Origin"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(origin), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // add_strings_row_to_table(*m_imgui, _u8L("Normal"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(normal), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // data_row_count = 2; + // break; + // } + // } + // } + // else { + // if (m_hover_id != -1 && m_curr_point_on_feature_position.has_value()) { + // Vec3d position = m_volume_matrix * *m_curr_point_on_feature_position; + // if (use_inches) + // position = ObjectManipulation::mm_to_in * position; + // add_strings_row_to_table(*m_imgui, _u8L("Position"), ImGuiWrapper::COL_ORANGE_LIGHT, format_vec3(position), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // data_row_count = 1; + // } + // } - // add dummy rows to keep dialog size fixed - for (unsigned int i = data_row_count; i < max_data_row_count; ++i) { - add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); - } - ImGui::EndTable(); - } + // // add dummy rows to keep dialog size fixed + // for (unsigned int i = data_row_count; i < max_data_row_count; ++i) { + // add_strings_row_to_table(*m_imgui, " ", ImGuiWrapper::COL_ORANGE_LIGHT, " ", ImGui::GetStyleColorVec4(ImGuiCol_Text)); + // } + // ImGui::EndTable(); + //} ImGui::Separator(); const ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersH; From 18edc71254076326ad470280e3f8115607eedd86 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 25 Oct 2022 15:54:52 +0200 Subject: [PATCH 313/327] 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 5be1efed12..4c83e1f0d2 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 7695a902d6..802cd3006e 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 e33c5c897c..e0fc4d3e87 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 435d8dc6e4..9734f1b77f 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 79483e4033..f8ead27f9e 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 f711ecf03c..5ec066f441 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 186b74febc..9db2ed1b1f 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 314/327] 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 7e3b709d62..14b31d7c50 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 e0fc4d3e87..35b6a92cec 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 315/327] 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 29374974bd..8056a01d3a 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 8c7b1db75d..164679645a 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() From cdf07c3cce81fd2653c571809a59cc9518ec3445 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 27 Oct 2022 15:34:23 +0200 Subject: [PATCH 316/327] Fix for #8800 - Switching to a physical printer based on same printer preset requires reslicing --- src/slic3r/GUI/Plater.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22c819ab47..656a7f3379 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4191,8 +4191,10 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) wxGetApp().get_tab(preset_type)->select_preset(preset_name); } - // update plater with new config - q->on_config_change(wxGetApp().preset_bundle->full_config()); + if (preset_type != Preset::TYPE_PRINTER || select_preset) { + // update plater with new config + q->on_config_change(wxGetApp().preset_bundle->full_config()); + } if (preset_type == Preset::TYPE_PRINTER) { /* Settings list can be changed after printer preset changing, so * update all settings items for all item had it. From ece63f5d81aa492c2fe358030a2aa5b0edfa1099 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 27 Oct 2022 17:16:44 +0200 Subject: [PATCH 317/327] Follow up previous https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/cdf07c3cce81fd2653c571809a59cc9518ec3445 - code improvements --- src/slic3r/GUI/Plater.cpp | 7 ------- src/slic3r/GUI/PresetComboBoxes.cpp | 19 +++++++++++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 656a7f3379..6fb7988906 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4180,13 +4180,6 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) combo->update(); } else if (select_preset) { - if (preset_type == Preset::TYPE_PRINTER) { - PhysicalPrinterCollection& physical_printers = wxGetApp().preset_bundle->physical_printers; - if(combo->is_selected_physical_printer()) - preset_name = physical_printers.get_selected_printer_preset_name(); - else - physical_printers.unselect_printer(); - } wxWindowUpdateLocker noUpdates(sidebar->presets_panel()); wxGetApp().get_tab(preset_type)->select_preset(preset_name); } diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index 65469e6a03..c63c20685a 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -515,12 +515,24 @@ bool PresetComboBox::is_selected_physical_printer() bool PresetComboBox::selection_is_changed_according_to_physical_printers() { - if (m_type != Preset::TYPE_PRINTER || !is_selected_physical_printer()) + if (m_type != Preset::TYPE_PRINTER) return false; - PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + const std::string selected_string = into_u8(this->GetString(this->GetSelection())); + PhysicalPrinterCollection& physical_printers = m_preset_bundle->physical_printers; + Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); - std::string selected_string = this->GetString(this->GetSelection()).ToUTF8().data(); + if (!is_selected_physical_printer()) { + if (!physical_printers.has_selection()) + return false; + + const bool is_changed = selected_string == physical_printers.get_selected_printer_preset_name(); + if (is_changed) + tab->select_preset(selected_string); + physical_printers.unselect_printer(); + + return is_changed; + } std::string old_printer_full_name, old_printer_preset; if (physical_printers.has_selection()) { @@ -548,7 +560,6 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers() return true; } - Tab* tab = wxGetApp().get_tab(Preset::TYPE_PRINTER); if (tab) tab->select_preset(preset_name, false, old_printer_full_name); return true; From e32d03318dd5d8066a9a232baa152cddee6fe1d7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 31 Oct 2022 10:42:54 +0100 Subject: [PATCH 318/327] Fix for #9104 - MMU can't change extruder when Split to Objects is used in PS2.5. --- src/libslic3r/Model.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 3c4e004544..52ddbea9b4 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1661,6 +1661,12 @@ void ModelObject::split(ModelObjectPtrs* new_objects) new_object->add_instance(*model_instance); ModelVolume* new_vol = new_object->add_volume(*volume, std::move(mesh)); + // Invalidate extruder value in volume's config, + // otherwise there will no way to change extruder for object after splitting, + // because volume's extruder value overrides object's extruder value. + if (new_vol->config.has("extruder")) + new_vol->config.set_key_value("extruder", new ConfigOptionInt(0)); + for (ModelInstance* model_instance : new_object->instances) { #if ENABLE_WORLD_COORDINATE Vec3d shift = model_instance->get_transformation().get_matrix_no_offset() * new_vol->get_offset(); From 7742ebb81362f596fa4baeaa4692fc7d8bd06384 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 31 Oct 2022 12:55:43 +0100 Subject: [PATCH 319/327] ObjectList: Fixed position of the added sub-object, when it is Slab --- src/slic3r/GUI/GUI_ObjectList.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 164679645a..d6bbbeba11 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1698,11 +1698,16 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb)); #endif // ENABLE_WORLD_COORDINATE // Set the modifier position. - auto offset = (type_name == "Slab") ? - // Slab: Lift to print bed - Vec3d(0., 0., 0.5 * mesh_bb.size().z() + instance_bb.min.z() - v->get_instance_offset().z()) : + Vec3d offset; + if (type_name == "Slab") { + Vec3d inst_center = instance_bb.center() - v->get_instance_offset(); + // Slab: Lift to print bed and and push to the center of instance + offset = Vec3d(inst_center.x(), inst_center.y(), 0.5 * mesh_bb.size().z() + instance_bb.min.z() - v->get_instance_offset().z()); + } + else { // Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed. - Vec3d(instance_bb.max.x(), instance_bb.min.y(), instance_bb.min.z()) + 0.5 * mesh_bb.size() - v->get_instance_offset(); + offset = Vec3d(instance_bb.max.x(), instance_bb.min.y(), instance_bb.min.z()) + 0.5 * mesh_bb.size() - v->get_instance_offset(); + } #if ENABLE_WORLD_COORDINATE new_volume->set_offset(v->get_instance_transformation().get_matrix_no_offset().inverse() * offset); #else From f5e782233e172c0d807bfd303a2936be18631f4e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 1 Nov 2022 09:15:38 +0100 Subject: [PATCH 320/327] Tech ENABLE_RAYCAST_PICKING - Fixed update of raycaster active state in GLCanvas3D::reload_scene() --- src/slic3r/GUI/GLCanvas3D.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 25ac882955..619f09901c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2438,8 +2438,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // refresh volume raycasters for picking m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume); for (size_t i = 0; i < m_volumes.volumes.size(); ++i) { - assert(m_volumes.volumes[i]->mesh_raycaster != nullptr); - add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *m_volumes.volumes[i]->mesh_raycaster, m_volumes.volumes[i]->world_matrix()); + const GLVolume* v = m_volumes.volumes[i]; + assert(v->mesh_raycaster != nullptr); + std::shared_ptr raycaster = add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *v->mesh_raycaster, v->world_matrix()); + raycaster->set_active(v->is_active); } // refresh gizmo elements raycasters for picking From 8914dfa1f654c663ef14635725ab3f8b82c686b4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 1 Nov 2022 14:55:19 +0100 Subject: [PATCH 321/327] Next fix, related to #8800 * PrintApply: Suppress to apply print when just a physical printer was changed, but printer preset stays the same * Tab: Layout the buttons bar when switch from the physical printer to printer preset and vice versa --- src/libslic3r/PrintApply.cpp | 5 ++++ src/slic3r/GUI/PresetComboBoxes.cpp | 9 ++++--- src/slic3r/GUI/Tab.cpp | 41 ++++++++++++++++------------- src/slic3r/GUI/Tab.hpp | 1 + 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index c3792779c3..330ad533c5 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -988,6 +988,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ DynamicPrintConfig filament_overrides; t_config_option_keys print_diff = print_config_diffs(m_config, new_full_config, filament_overrides); t_config_option_keys full_config_diff = full_print_config_diffs(m_full_print_config, new_full_config); + // If just a physical printer was changed, but printer preset is the same, then there is no need to apply whole print + // see https://github.com/prusa3d/PrusaSlicer/issues/8800 + if (full_config_diff.size() == 1 && full_config_diff[0] == "physical_printer_settings_id") + full_config_diff.clear(); + // Collect changes to object and region configs. t_config_option_keys object_diff = m_default_object_config.diff(new_full_config); t_config_option_keys region_diff = m_default_region_config.diff(new_full_config); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index c63c20685a..200cd8a30c 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -527,10 +527,9 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers() return false; const bool is_changed = selected_string == physical_printers.get_selected_printer_preset_name(); + physical_printers.unselect_printer(); if (is_changed) tab->select_preset(selected_string); - physical_printers.unselect_printer(); - return is_changed; } @@ -547,16 +546,18 @@ bool PresetComboBox::selection_is_changed_according_to_physical_printers() // if new preset wasn't selected, there is no need to call update preset selection if (old_printer_preset == preset_name) { + tab->update_preset_choice(); + wxGetApp().plater()->show_action_buttons(false); + // we need just to update according Plater<->Tab PresetComboBox if (dynamic_cast(this)!=nullptr) { - wxGetApp().get_tab(m_type)->update_preset_choice(); // Synchronize config.ini with the current selections. m_preset_bundle->export_selections(*wxGetApp().app_config); + this->update(); } else if (dynamic_cast(this)!=nullptr) wxGetApp().sidebar().update_presets(m_type); - this->update(); return true; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 8f38a1525e..7e441342f8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -215,26 +215,30 @@ void Tab::create_preset_tab() sizer->Add(m_hsizer, 0, wxEXPAND | wxBOTTOM, 3); m_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); m_hsizer->AddSpacer(int(4*scale_factor)); - m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(4*scale_factor)); - m_hsizer->Add(m_btn_rename_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(4 * scale_factor)); - m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); + + m_h_buttons_sizer = new wxBoxSizer(wxHORIZONTAL); + m_h_buttons_sizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(4*scale_factor)); + m_h_buttons_sizer->Add(m_btn_rename_preset, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(4 * scale_factor)); + m_h_buttons_sizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); if (m_btn_edit_ph_printer) { - m_hsizer->AddSpacer(int(4 * scale_factor)); - m_hsizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(4 * scale_factor)); + m_h_buttons_sizer->Add(m_btn_edit_ph_printer, 0, wxALIGN_CENTER_VERTICAL); } - m_hsizer->AddSpacer(int(/*16*/8 * scale_factor)); - m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(8 * scale_factor)); - m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(32 * scale_factor)); - m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(32 * scale_factor)); - m_hsizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(int(8*scale_factor)); - m_hsizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(/*16*/8 * scale_factor)); + m_h_buttons_sizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(8 * scale_factor)); + m_h_buttons_sizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(32 * scale_factor)); + m_h_buttons_sizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(32 * scale_factor)); + m_h_buttons_sizer->Add(m_search_btn, 0, wxALIGN_CENTER_VERTICAL); + m_h_buttons_sizer->AddSpacer(int(8*scale_factor)); + m_h_buttons_sizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL); + + m_hsizer->Add(m_h_buttons_sizer, 1, wxEXPAND); m_hsizer->AddSpacer(int(16*scale_factor)); // m_hsizer->AddStretchSpacer(32); // StretchSpacer has a strange behavior under OSX, so @@ -3192,6 +3196,7 @@ void Tab::update_btns_enabling() if (m_btn_edit_ph_printer) m_btn_edit_ph_printer->SetToolTip( m_preset_bundle->physical_printers.has_selection() ? _L("Edit physical printer") : _L("Add physical printer")); + m_h_buttons_sizer->Layout(); } void Tab::update_preset_choice() diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 448e4be050..5adeb278a4 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -173,6 +173,7 @@ protected: ScalableButton* m_btn_edit_ph_printer {nullptr}; ScalableButton* m_btn_hide_incompatible_presets; wxBoxSizer* m_hsizer; + wxBoxSizer* m_h_buttons_sizer; wxBoxSizer* m_left_sizer; wxTreeCtrl* m_treectrl; From 4325608a56d3638b74f1f2ad0616eef26200c9dc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 1 Nov 2022 16:52:42 +0100 Subject: [PATCH 322/327] Disable webrequest for wxWidgets as we don't need it It requires CURL and doesn't like our static build so it links to the system curl instead which is bad. --- deps/wxWidgets/wxWidgets.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 8056a01d3a..95f54c1a29 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -38,6 +38,7 @@ prusaslicer_add_cmake_project(wxWidgets -DwxUSE_LIBSDL=OFF -DwxUSE_XTEST=OFF -DwxUSE_GLCANVAS_EGL=OFF + -DwxUSE_WEBREQUEST=OFF ) if (MSVC) From da5a9277faa5cbc54eec0bba04942d7e8ba1cdaf Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 2 Nov 2022 08:50:12 +0100 Subject: [PATCH 323/327] Tech ENABLE_MEASURE_GIZMO set as default --- src/imgui/imconfig.h | 22 ++-------------------- src/libslic3r/Measure.cpp | 4 ---- src/libslic3r/Measure.hpp | 4 ---- src/libslic3r/MeasureUtils.hpp | 4 ---- src/libslic3r/Technologies.hpp | 5 ++--- src/slic3r/GUI/GLCanvas3D.cpp | 8 +------- src/slic3r/GUI/GLCanvas3D.hpp | 14 -------------- src/slic3r/GUI/GUI_Utils.hpp | 2 -- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 4 ---- src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp | 4 ---- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 -- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 10 ---------- src/slic3r/GUI/ImGuiWrapper.cpp | 4 ---- src/slic3r/GUI/ImGuiWrapper.hpp | 2 -- src/slic3r/GUI/Plater.cpp | 2 -- 15 files changed, 5 insertions(+), 86 deletions(-) diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 2fb6d7daf7..6dabace5b5 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -157,8 +157,8 @@ namespace ImGui const wchar_t InfoMarker = 0x2603; const wchar_t SliderFloatEditBtnIcon = 0x2604; const wchar_t SliderFloatEditBtnPressedIcon = 0x2605; -#if ENABLE_MEASURE_GIZMO const wchar_t ClipboardBtnIcon = 0x2606; + const wchar_t LegendTravel = 0x2701; const wchar_t LegendWipe = 0x2702; const wchar_t LegendRetract = 0x2703; @@ -175,25 +175,7 @@ namespace ImGui const wchar_t ExpandBtn = 0x2714; const wchar_t CollapseBtn = 0x2715; const wchar_t InfoMarkerSmall = 0x2716; -#else - const wchar_t LegendTravel = 0x2606; - const wchar_t LegendWipe = 0x2607; - const wchar_t LegendRetract = 0x2608; - const wchar_t LegendDeretract = 0x2609; - const wchar_t LegendSeams = 0x2610; - const wchar_t LegendToolChanges = 0x2611; - const wchar_t LegendColorChanges = 0x2612; - const wchar_t LegendPausePrints = 0x2613; - const wchar_t LegendCustomGCodes = 0x2614; - const wchar_t LegendCOG = 0x2615; - 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; - const wchar_t InfoMarkerSmall = 0x2621; -#endif // ENABLE_MEASURE_GIZMO -// void MyFunction(const char* name, const MyMatrix44& v); + // void MyFunction(const char* name, const MyMatrix44& v); } diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index c060ae8938..f64b79e22f 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -5,8 +5,6 @@ #include "libslic3r/Geometry/Circle.hpp" #include "libslic3r/SurfaceMesh.hpp" -#if ENABLE_MEASURE_GIZMO - namespace Slic3r { namespace Measure { @@ -1082,5 +1080,3 @@ void AngleAndEdges::transform(const Transform3d& trafo) { } // namespace Measure } // namespace Slic3r - -#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index ff1757d7ae..d1a0e3866c 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -1,8 +1,6 @@ #ifndef Slic3r_Measure_hpp_ #define Slic3r_Measure_hpp_ -#if ENABLE_MEASURE_GIZMO - #include #include @@ -197,5 +195,3 @@ inline bool are_perpendicular(const SurfaceFeature& f1, const SurfaceFeature& f2 } // namespace Slic3r #endif // Slic3r_Measure_hpp_ - -#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/MeasureUtils.hpp b/src/libslic3r/MeasureUtils.hpp index 34fea9855c..0ab4ac121d 100644 --- a/src/libslic3r/MeasureUtils.hpp +++ b/src/libslic3r/MeasureUtils.hpp @@ -1,8 +1,6 @@ #ifndef Slic3r_MeasureUtils_hpp_ #define Slic3r_MeasureUtils_hpp_ -#if ENABLE_MEASURE_GIZMO - #include namespace Slic3r { @@ -386,5 +384,3 @@ inline Vec3d get_orthogonal(const Vec3d& v, bool unitLength) } // namespace Measure #endif // Slic3r_MeasureUtils_hpp_ - -#endif // ENABLE_MEASURE_GIZMO diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 10db1e57ec..e390ff209e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -26,6 +26,8 @@ #define ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW 0 // Disable using instanced models to render options in gcode preview #define DISABLE_GCODEVIEWER_INSTANCED_MODELS 1 +// Enable Measure Gizmo debug window +#define ENABLE_MEASURE_GIZMO_DEBUG 0 // Enable rendering of objects using environment map @@ -64,9 +66,6 @@ // Enable picking using raytracing #define ENABLE_RAYCAST_PICKING (1 && ENABLE_LEGACY_OPENGL_REMOVAL) #define ENABLE_RAYCAST_PICKING_DEBUG (0 && ENABLE_RAYCAST_PICKING) -// Enable Measure Gizmo -#define ENABLE_MEASURE_GIZMO (1 && ENABLE_RAYCAST_PICKING) -#define ENABLE_MEASURE_GIZMO_DEBUG (0 && ENABLE_MEASURE_GIZMO) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3475221818..440f97facb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5441,15 +5441,9 @@ void GLCanvas3D::_picking_pass() const GLVolume* volume = m_volumes.volumes[hit.raycaster_id]; if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) { // do not add the volume id if any gizmo is active and CTRL is pressed - if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) { + if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) m_hover_volume_idxs.emplace_back(hit.raycaster_id); -#if !ENABLE_MEASURE_GIZMO - m_gizmos.set_hover_id(-1); -#endif // !ENABLE_MEASURE_GIZMO - } -#if ENABLE_MEASURE_GIZMO m_gizmos.set_hover_id(-1); -#endif // ENABLE_MEASURE_GIZMO } } else diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 7ce488ae54..a5ec9c1914 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -19,9 +19,7 @@ #if ENABLE_RAYCAST_PICKING #include "SceneRaycaster.hpp" #endif // ENABLE_RAYCAST_PICKING -#if ENABLE_MEASURE_GIZMO #include "GUI_Utils.hpp" -#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/Slicing.hpp" @@ -138,18 +136,6 @@ private: wxTimer* m_timer; }; -#if !ENABLE_MEASURE_GIZMO -class KeyAutoRepeatFilter -{ - size_t m_count{ 0 }; - -public: - void increase_count() { ++m_count; } - void reset_count() { m_count = 0; } - bool is_first() const { return m_count == 0; } -}; -#endif // !ENABLE_MEASURE_GIZMO - wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); using Vec2dEvent = Event; diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index e2152edd6d..55ca432480 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -416,7 +416,6 @@ public: ~TaskTimer(); }; -#if ENABLE_MEASURE_GIZMO class KeyAutoRepeatFilter { size_t m_count{ 0 }; @@ -426,7 +425,6 @@ public: void reset_count() { m_count = 0; } bool is_first() const { return m_count == 0; } }; -#endif // ENABLE_MEASURE_GIZMO }} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 58e23b695f..74be878f11 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -19,8 +19,6 @@ #include -#if ENABLE_MEASURE_GIZMO - namespace Slic3r { namespace GUI { @@ -1691,5 +1689,3 @@ void GLGizmoMeasure::on_unregister_raycasters_for_picking() } // namespace GUI } // namespace Slic3r - -#endif // ENABLE_MEASURE_GIZMO diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index 9db2a73aa3..7d20ca26ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -1,8 +1,6 @@ #ifndef slic3r_GLGizmoMeasure_hpp_ #define slic3r_GLGizmoMeasure_hpp_ -#if ENABLE_MEASURE_GIZMO - #include "GLGizmoBase.hpp" #include "slic3r/GUI/GLModel.hpp" #include "slic3r/GUI/GUI_Utils.hpp" @@ -162,6 +160,4 @@ protected: } // namespace GUI } // namespace Slic3r -#endif // ENABLE_MEASURE_GIZMO - #endif // slic3r_GLGizmoMeasure_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index f2a668f870..7579402261 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -24,10 +24,8 @@ enum class SLAGizmoEventType : unsigned char { Dragging, Delete, SelectAll, -#if ENABLE_MEASURE_GIZMO CtrlDown, CtrlUp, -#endif // ENABLE_MEASURE_GIZMO ShiftUp, AltUp, ApplyChanges, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index f0617f5248..51a0386ed3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -21,9 +21,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" -#if ENABLE_MEASURE_GIZMO #include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp" -#endif // ENABLE_MEASURE_GIZMO #include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" @@ -109,9 +107,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoSeam(m_parent, "seam.svg", 8)); m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, "mmu_segmentation.svg", 9)); m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "cut.svg", 10)); -#if ENABLE_MEASURE_GIZMO m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", 11)); -#endif // ENABLE_MEASURE_GIZMO m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); @@ -294,10 +290,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); -#if ENABLE_MEASURE_GIZMO else if (m_current == Measure) return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); -#endif // ENABLE_MEASURE_GIZMO else if (m_current == Cut) return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); else @@ -631,11 +625,9 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) processed = true; } } -#if ENABLE_MEASURE_GIZMO else if (m_current == Measure && keyCode == WXK_CONTROL) { gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), false); } -#endif // ENABLE_MEASURE_GIZMO // if (processed) // m_parent.set_cursor(GLCanvas3D::Standard); @@ -670,11 +662,9 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) if (simplify != nullptr) processed = simplify->on_esc_key_down(); } -#if ENABLE_MEASURE_GIZMO else if (m_current == Measure && keyCode == WXK_CONTROL) { gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), true); } -#endif // ENABLE_MEASURE_GIZMO } if (processed) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 14c9b641f7..01cd8ec30c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -56,9 +56,7 @@ static const std::map font_icons = { {ImGui::PreferencesHoverButton, "notification_preferences_hover"}, {ImGui::SliderFloatEditBtnIcon, "edit_button" }, {ImGui::SliderFloatEditBtnPressedIcon, "edit_button_pressed" }, -#if ENABLE_MEASURE_GIZMO {ImGui::ClipboardBtnIcon , "copy_menu" }, -#endif // ENABLE_MEASURE_GIZMO {ImGui::ExpandBtn , "expand_btn" }, {ImGui::CollapseBtn , "collapse_btn" }, {ImGui::RevertButton , "undo" }, @@ -1129,12 +1127,10 @@ ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id) return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr; } -#if ENABLE_MEASURE_GIZMO void ImGuiWrapper::disable_background_fadeout_animation() { GImGui->DimBgRatio = 1.0f; } -#endif // ENABLE_MEASURE_GIZMO ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color) { diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 8be16fcd0b..040e5e4916 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -130,9 +130,7 @@ public: void set_requires_extra_frame() { m_requires_extra_frame = true; } void reset_requires_extra_frame() { m_requires_extra_frame = false; } -#if ENABLE_MEASURE_GIZMO void disable_background_fadeout_animation(); -#endif // ENABLE_MEASURE_GIZMO static ImU32 to_ImU32(const ColorRGBA& color); static ImVec4 to_ImVec4(const ColorRGBA& color); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ee3a3c3a9f..63b0a4e217 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3034,9 +3034,7 @@ void Plater::priv::delete_all_objects_from_model() gcode_result.reset(); view3D->get_canvas3d()->reset_sequential_print_clearance(); -#if ENABLE_MEASURE_GIZMO view3D->get_canvas3d()->reset_all_gizmos(); -#endif // ENABLE_MEASURE_GIZMO m_worker.cancel_all(); From c1fec355d3dd8c3cf0fbfc609379a45ef649b155 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 2 Nov 2022 09:42:50 +0100 Subject: [PATCH 324/327] Initial Anker bundle https://github.com/prusa3d/PrusaSlicer/pull/9075 --- resources/profiles/Anker.idx | 2 + resources/profiles/Anker.ini | 393 ++++++++ resources/profiles/Anker/M5-bed.stl | Bin 0 -> 36084 bytes resources/profiles/Anker/M5-texture.svg | 1003 +++++++++++++++++++++ resources/profiles/Anker/M5_thumbnail.png | Bin 0 -> 37200 bytes 5 files changed, 1398 insertions(+) create mode 100644 resources/profiles/Anker.idx create mode 100644 resources/profiles/Anker.ini create mode 100644 resources/profiles/Anker/M5-bed.stl create mode 100644 resources/profiles/Anker/M5-texture.svg create mode 100644 resources/profiles/Anker/M5_thumbnail.png diff --git a/resources/profiles/Anker.idx b/resources/profiles/Anker.idx new file mode 100644 index 0000000000..4a988bbdff --- /dev/null +++ b/resources/profiles/Anker.idx @@ -0,0 +1,2 @@ +min_slic3r_version = 2.6.0-alpha1 +1.0.0 Initial Version diff --git a/resources/profiles/Anker.ini b/resources/profiles/Anker.ini new file mode 100644 index 0000000000..c7408f6e4d --- /dev/null +++ b/resources/profiles/Anker.ini @@ -0,0 +1,393 @@ +# Print profiles for the AnkerMake printers. +# https://github.com/prusa3d/PrusaSlicer/pull/9075 by @just-trey + +[vendor] +# Vendor name will be shown by the Config Wizard. +name = Anker +# Configuration version of this file. Config file will only be installed, if the config_version differs. +# This means, the server may force the PrusaSlicer configuration to be downgraded. +config_version = 1.0.0 +# Where to get the updates from? +config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anker/ + +# The printer models will be shown by the Configuration Wizard in this order, +# also the first model installed & the first nozzle installed will be activated after install. +# Printer model name will be shown by the installation wizard. + +[printer_model:M5] +name = AnkerMake M5 +variants = 0.4 +technology = FFF +family = AnkerMake +bed_model = M5-bed.stl +bed_texture = M5-texture.svg +default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER + +# All presets starting with asterisk, for example *common*, are intermediate and they will +# not make it into the user interface. + +# Common print preset +[print:*common*] +avoid_crossing_perimeters = 0 +bridge_acceleration = 2500 +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 150 +brim_separation = 0.1 +brim_type = outer_only +brim_width = 0 +clip_multipart_objects = 1 +complete_objects = 0 +default_acceleration = 2500 +dont_support_bridges = 1 +elefant_foot_compensation = 0.1 +ensure_vertical_shell_thickness = 1 +external_perimeter_speed = 150 +external_perimeters_first = 0 +extra_perimeters = 0 +extruder_clearance_height = 30 +extruder_clearance_radius = 45 +fill_angle = 45 +fill_density = 15% +fill_pattern = cubic +first_layer_acceleration = 2500 +first_layer_acceleration_over_raft = 0 +first_layer_extrusion_width = 200% +first_layer_speed = 50% +first_layer_speed_over_raft = 30 +gap_fill_enabled = 1 +gap_fill_speed = 150 +gcode_comments = 0 +infill_acceleration = 2500 +infill_anchor = 600% +infill_anchor_max = 50 +infill_every_layers = 1 +infill_extruder = 1 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 23% +infill_speed = 250 +interface_shells = 0 +max_print_speed = 250 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = {input_filename_base}_{digits(layer_height,1,2)}mm_{filament_type[0]}_{printer_model}.gcode +overhangs = 1 +perimeter_acceleration = 2500 +perimeter_extruder = 1 +perimeter_extrusion_width = 0 +perimeter_generator = arachne +perimeter_speed = 250 +perimeters = 3 +post_process = +print_settings_id = +raft_layers = 0 +resolution = 0 +seam_position = aligned +single_extruder_multi_material_priming = 0 +skirt_distance = 3 +skirt_height = 1 +skirts = 3 +small_perimeter_speed = 150 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_speed = 175 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_contact_distance = 0.15 +support_material_enforce_layers = 0 +support_material_extruder = 0 +support_material_interface_contact_loops = 0 +support_material_interface_extruder = 0 +support_material_interface_layers = 2 +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_spacing = 2 +support_material_speed = 125 +support_material_synchronize_layers = 0 +support_material_threshold = 40 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thick_bridges = 1 +thin_walls = 0 +top_solid_infill_speed = 150 +travel_speed = 300 +travel_speed_z = 10 +wipe_tower = 0 +wipe_tower_bridging = 10 +wipe_tower_rotation_angle = 0 +wipe_tower_width = 60 +wipe_tower_x = 170 +wipe_tower_y = 140 +xy_size_compensation = 0 + +[print:*0.08mm*] +inherits = *common* +layer_height = 0.08 +first_layer_height = 0.12 +bottom_solid_layers = 9 +top_solid_layers = 11 +bridge_flow_ratio = 0.70 + +[print:*0.10mm*] +inherits = *common* +layer_height = 0.10 +first_layer_height = 0.14 +bottom_solid_layers = 7 +top_solid_layers = 9 +bridge_flow_ratio = 0.70 + +[print:*0.12mm*] +inherits = *common* +layer_height = 0.12 +first_layer_height = 0.16 +bottom_solid_layers = 6 +top_solid_layers = 7 +bridge_flow_ratio = 0.70 + +[print:*0.16mm*] +inherits = *common* +layer_height = 0.16 +first_layer_height = 0.20 +bottom_solid_layers = 5 +top_solid_layers = 7 +bridge_flow_ratio = 0.85 + +[print:*0.20mm*] +inherits = *common* +layer_height = 0.20 +first_layer_height = 0.24 +bottom_solid_layers = 4 +top_solid_layers = 5 + +[print:*0.24mm*] +inherits = *common* +layer_height = 0.24 +first_layer_height = 0.28 +bottom_solid_layers = 3 +top_solid_layers = 4 + +[print:*0.28mm*] +inherits = *common* +layer_height = 0.28 +first_layer_height = 0.28 +bottom_solid_layers = 3 +top_solid_layers = 4 + +[print:0.08 mm SUPERDETAIL (0.4 mm nozzle) @ANKER] +inherits = *0.08mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[print:0.10 mm HIGHDETAIL (0.4 mm nozzle) @ANKER] +inherits = *0.10mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[print:0.12 mm DETAIL (0.4 mm nozzle) @ANKER] +inherits = *0.12mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[print:0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER] +inherits = *0.16mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[print:0.20 mm NORMAL (0.4 mm nozzle) @ANKER] +inherits = *0.20mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[print:0.24 mm DRAFT (0.4 mm nozzle) @ANKER] +inherits = *0.24mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[print:0.28 mm SUPERDRAFT (0.4 mm nozzle) @ANKER] +inherits = *0.28mm* +compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 + +[filament:*common*] +cooling = 0 +compatible_printers = +extrusion_multiplier = 1 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_notes = "" +filament_settings_id = "" +filament_soluble = 0 +min_print_speed = 30 +slowdown_below_layer_time = 8 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_ANKERMAKE.*/ + +[filament:*PLA*] +inherits = *common* +bed_temperature = 60 +fan_below_layer_time = 100 +filament_colour = #DDDDDD +filament_type = PLA +filament_density = 1.24 +filament_cost = 20 +first_layer_bed_temperature = 60 +first_layer_temperature = 210 +fan_always_on = 1 +max_fan_speed = 100 +min_fan_speed = 100 +bridge_fan_speed = 100 +disable_fan_first_layers = 2 +temperature = 205 + +[filament:*PLA+*] +inherits = *common* +bed_temperature = 60 +fan_below_layer_time = 100 +filament_colour = #DDDDDD +filament_type = PLA +filament_density = 1.24 +filament_cost = 20 +first_layer_bed_temperature = 60 +first_layer_temperature = 220 +fan_always_on = 1 +max_fan_speed = 100 +min_fan_speed = 100 +bridge_fan_speed = 100 +disable_fan_first_layers = 2 +temperature = 210 + +[filament:*PET*] +inherits = *common* +bed_temperature = 70 +disable_fan_first_layers = 2 +fan_below_layer_time = 20 +filament_colour = #DDDDDD +filament_type = PETG +filament_density = 1.27 +filament_cost = 30 +first_layer_bed_temperature = 70 +first_layer_temperature = 240 +fan_always_on = 1 +max_fan_speed = 50 +min_fan_speed = 50 +bridge_fan_speed = 100 +temperature = 240 + +[filament:*ABS*] +inherits = *common* +bed_temperature = 90 +disable_fan_first_layers = 2 +fan_below_layer_time = 20 +filament_colour = #DDDDDD +filament_type = ABS +filament_density = 1.04 +filament_cost = 20 +first_layer_bed_temperature = 100 +first_layer_temperature = 245 +fan_always_on = 0 +max_fan_speed = 0 +min_fan_speed = 0 +bridge_fan_speed = 30 +top_fan_speed = 0 +temperature = 245 + +[filament:Generic PLA @ANKER] +inherits = *PLA* +filament_vendor = Generic + +[filament:Generic PLA+ @ANKER] +inherits = *PLA+* +filament_vendor = Generic + +[filament:Generic PETG @ANKER] +inherits = *PET* +filament_vendor = Generic + +[filament:Generic ABS @ANKER] +inherits = *ABS* +first_layer_bed_temperature = 90 +bed_temperature = 90 +filament_vendor = Generic + + +# Common printer preset +[printer:*common*] +printer_technology = FFF +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;{layer_z} +between_objects_gcode = +pause_print_gcode = +deretract_speed = 60 +extruder_colour = #FCE94F +extruder_offset = 0x0 +gcode_flavor = marlin +silent_mode = 0 +remaining_times = 0 +machine_max_acceleration_e = 2500 +machine_max_acceleration_extruding = 2500 +machine_max_acceleration_retracting = 2500 +machine_max_acceleration_travel = 1500,1250 +machine_max_acceleration_x = 2500 +machine_max_acceleration_y = 2500 +machine_max_acceleration_z = 2500 +machine_max_feedrate_e = 100 +machine_max_feedrate_x = 300 +machine_max_feedrate_y = 300 +machine_max_feedrate_z = 20 +machine_max_jerk_e = 3 +machine_max_jerk_x = 30 +machine_max_jerk_y = 30 +machine_max_jerk_z = 0.3 +machine_min_extruding_rate = 0 +machine_min_travel_rate = 0 +layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z} +max_print_height = 250 +printer_notes = +printer_settings_id = +retract_before_travel = 2 +retract_before_wipe = 70% +retract_layer_change = 1 +retract_length_toolchange = 1 +retract_lift = 0 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 60 +single_extruder_multi_material = 0 +thumbnails = 64x64,256x256 +thumbnails_format = JPG +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 0 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 +default_filament_profile = "Generic PLA+ @ANKER" +start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for nozzle temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime) +end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84 + +[printer:*M5*] +inherits = *common* +bed_shape = 0x0,235-0,235x235,0x235 +max_print_height = 250 +printer_model = M5 +retract_length = 1.25 +retract_speed = 60 +deretract_speed = 60 +retract_before_travel = 1 +retract_before_wipe = 0% +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANKERMAKE\nPRINTER_MODEL_M5 + +[printer:AnkerMake M5 (0.4 mm nozzle)] +inherits = *M5* +nozzle_diameter = 0.4 +printer_variant = 0.4 +min_layer_height = 0.08 +max_layer_height = 0.32 +retract_lift_above = 0.2 +default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER" \ No newline at end of file diff --git a/resources/profiles/Anker/M5-bed.stl b/resources/profiles/Anker/M5-bed.stl new file mode 100644 index 0000000000000000000000000000000000000000..72e12a23cd4624ccca34f1337631111be8b8793d GIT binary patch literal 36084 zcmb`Q4X`Ceb;p}Ue9AWqlv;jZ5pfq0BBF*MyczB)X!(hnh$|{c5)(xt8Uz&FMpoGc zblE2e0tOHljR;vl6G*XdhHDm9tPqS?DiJKy2#TRFL2zY-^y$;5|Novh^X{W6Q?>W? z-afx``fK{T`#Sgk``oce^!diLZsndBKe*omW9RBt+|zGc^)~~GFFxq=Xj0^jKb`EZd$B_@6 zI6Hg#$&p|mxSc!n>fek%de0w12;~_fSPE{j@%{I1ntjoEABZ;C2X5y!p7QYc#;dm^ z8$$$3!QGc1mWr@Zw+ka!YR{)VI=*PP$70^Z$fpm@bF;xx$G&0n__F72oBp67Vd=+tSJ(=oZQ^H_JnBT#@WZm8{hk2w88e^UKrsVGzoFw{wtcV zuldpjaU(@ps%{4f&jj2|bV_0cDYns#xr9a^-@v-oK32vM^Z9H*s+)smA=Exas5sZk zQZhua6v{EA#Z}s1DZDH55iEuGQHX2LSkhc{{-Lq7L#l4!c8={iw|duQ&6{r7D-sBM z-Le$E?}VyBikA6-bd&s;Y@@U{91ji^++ySQf0}B3weve-Jdh@3%#jN75ulv|(TRCc zN|->6Iv>GO+?Fs?mJ*gi+m+e9xZYp+l3E4Sg!F&&9uLwf;CuoT>Q z2P5J8iI6whwi3CQQ4lO8?(a@Lky``RFX4lfx=jj1j79$N75f~t`?{pJ)D*?N#k?z} zHcESDKtt9~)GfZ3n6LhaFK@VWAMKa?>cdxz*X%7ZbPiJN?hwA8s9St5#DRbM(b-p? zrahS*@4I|_>Dg)nQqIBMA$&hkxAIe_@4ja1(%NRI8ALpD)gwb?GEAliMqx2LY#W)lIDjOYc1Mr{^GcL zLTy0GIk-E7?p#C$NW%`{AQd!`6x<=%?eWs$ zu+Ki*95Zg7{rgzPSd#%R5+7W8$@p#cqL_nBuqNj|e)#fc*PFF}#Xg|x7H+X{^TEe8 z*N-lZ;!W5 zHi#g-&ao8SVk13XQkQ)=tzZw*;wp*lesJ#i;UlN!q;z6}xDao>;jCUMrXwNJ@%>VXqHgiM5MP}+Y4(=) zX?=Ce;jgmd^_h(!l4Y0mkc)>r2pwtD=MD>WXF znuqZHMBUS8mV!IzwZ}_oXCHo}SW^Uo zv^*uT!~LtqZ@f=afoTv{Oe@wDWiui1IC3YI>Jp~Oxral{*Sz{mhX~8oLDwzZB6axH zp>BWiN2bNiK5*Bq~@(jaqZR*%%_ET{0=S#Ri@-bzwtWHKidfu99HA($?0$)>O}o?-DE}J%BmH z92+e4)aR}pKl;GpSQeZ+=iGH0K9Ocpk7=O-feMM>gC(d<; zv`%Cn(9Xdvp$a3fDvj01$GNRfo5|y|CrgyHbH~4NdG#;f)H)G*ojc+yGil^ixA?xB zR_w#Xa9Uk?%S;-dm1)Iv?6+5;-yUl?_&CQjzL!vip8f=)q@8>0>XWORp8t_(1A3kN z#9cFKJXp8*UTjPzO4_;g`>&{;ag5fYtQT(QUVHCM9$5|(ECqMUk5n9e!|4tibvuXf z0SW(n!#?7+5hQC8_1d=*7YKD5f$ud`lZcWID~BPi-ux~dK_VoYqB{Q6c$hs2OMJY= z!+a#>Mo2uY?J{Zk>l?{i8(Xpz(>c%M2$Fq3Yq>H_xgCLAFb7l z#`hAc$wWyz_dh!?t+sjgq0xraqB{DmTMi-I65oEpxqey*JicOGNGqlxEiPY4`(Yzq z*x;Uw$c3?U73LE=@o=u6mb4);&d@`uZs86ir#!}0-MKGX@wkse z(wkOZ%2oe6QPy3_O8is4=cuE)Z!H*Kp|&ign9lNELWR1dd}~p!NNGOe_IJZwFJ3D#5+lIOPO!31l{ zgtt65LQ00N-IQ_4L~xY-MCjGL(fCnGY=mXK974FesVk-3RS%x!Ay?<;wboAO`yF0W9kh ze(yjh{a*i;1o{cwt4V}`aa3fX;vNU+6!UBYKIPooU@xP=d>hY;b$b9INZAI6at?7} zHXz0GiIjuEj<0w&lUqylQ9_95@eO=*rpWpw(9}&U`0$)A_iDtz(2Ov*=iJUypu+nB z8)c|KNT`PTSLh*?^l;yYLj||k80yJz?*(olj+r@e_A_Vd4v79$fY9dCioh@dnhB(o z&W-XKOK^wTI$in0_aMYou17I(^%<*2wz|lSuReF!>XEHfvfh$dS1qaj<_;aJu@BsJ z%bJ|)Cs>Nz5+)u&E?>NQWGk+$7jDUq$wb{klMvh^XFHc`^e@NbR+1mue-s-$1Ia$1HQg+0J(w;=5!hANQgj?^GX|^40crnnm!SwL%X!gO5&(KCuXrqnv;VXHctsl)a+sidW z?2$3SQgBNS@_c(gp|y|o2Fq0dsmZKQ%{1I%gVzoE39UuZvkW`WD%*J`wX^lSnTA_z zOeX3UnuOrq>oYd39$7COQenSmw!JW-ZlOttuv=u>b^>l(eAdYN?T~T~?u8NIL{-)# z1h2%s^w_gTw%P}&aL#LxU?0$R3%3gouid@nhOK2-W&`0|Wp@)=g zuv8e;WZNKwi|d0uMBMQ^I_R0U8#fu5LxlabS?bDt)}}NR8&~bIZo?HXihEK$eByjk zwukS9SabeKvlpfvri)KqoAVq}VVK5ONjWCXy4Ku0nJ%9DBYjfF!lyh)* z2;WcCExs3GhjXTy%l6TeFYC9PN$(&_Iad-cKfdWF;*8hG-6(76VgpY}gwa-(dev)Y zQXM12FLqqo-2R-nYU$w<+D8f@z88Y_ixvb+op$O>s$DyTgOqbD1vlcLtpWQW9cX-IM?D_MoMY%`Qults5X4zGTWVy3AO>Z zq}601u8b#5LL9!+Db?%#7yNa!vD1B*kDhhBo=0 z3xUTs>=W{Nq^I-hmNi9ruT(r!J+kwzEXB0QV#LBe;#NAAn5-$D`mOBLZ#Z8&vXjLi z;@QqjFNBEad6Omy)ACzRuFgGIPcI*^=<$)AkY;;uFN_E~WwRzB!YS;^PGKK;=a!M3 z4Tn_NXZa+F&_BwW#KvB)SY9o^TX((eUOhIllj)GETeufSgnjQ>lMt`iby;;?I+6S1 z*KZ!#d3i{Mo&8Uea1NTp##2`=sos*#34d|w(UG0vhg99dy)eQ#XcFSe+oq~dJ|&;X z9qIiAbqgtWFN}ycAtX&ggqski>F$J)-op^@GRXGA>D(tp#2X!wCb98`O(#wtxKiig zPPyvAk=-*9&hTb?a4(E-4w{5$#_JB*vQ+C5*(s-W2}{*&NeHD+JoTI3&VJft!(X4; zk8A_)c{pj8>#;|nXIHxaY)2%Cze5H`RMQ!YSA+6 zOb@3nhaIVlc!-g*-eoDKIb3CafasPhFr^KYCC>9ssO0>4&T|mm_hZNg-if*eJ)aE_ z-S>RR2EXT^=d%H#TVD;?Kpn+($$U0IbnD$A8>nl!PMpsMh;I8aWCLvm*X{G!0MTs+ zhistj<2H(H_)yv_ZiTswfrfr@m1P$+x3y{`_Dg~d?tiG;`ab1b8*U@1?pYJ}^~v2L zxrZb+c3b(sVSR#W`5zvQhe^qsO1smA!B#Sd-b%UK#tq9qYv0 zpPxfypMFA8_pq<6+i>vSdMYJIC8BQS)`;zYveh!tiSNyV{w?FdM5r0YKU<+RGbO=N z;XNOJ>OL3k4HGOSH*6gE>`4Sm$-N(cS3Mqbw0z1?v6S3j^3{hQn?SIX++VWo(VKe+ zx$$KEIgdmiPSKLsGu#Mt{k|LHstkO>Y#q_~UgGgcxTk1p!#0(;5fZ&ONJGMIk-`(- z!LhwX&|9~x3G(T!%>+v!-Kb^=Xla9`kT>%YEQS1^k6F;TB@I7cOf)wtlZzO5juGGjdZ1JY^1|Byzc$FLgt`JJuSN%drqggxePO zUwc9fSr@J3e(;TBIUB_XawEjqa*>Wy-!sVA5A*n6ecVCaqFcLi>V`Q$dL zY_EjDnsCU2Q0MB+fw)hGTTUs7^~QCFJi1idLFswhyUjG*Vt@$WPv|JjT2bfj*?8jg zyH{#m5@I;g7E=4_W)GUg1`)oW(6Is9+K?a9R{MmyWTZKzeRZ=3O=5!x-%se+0C|4@ z*QTnYo~iYf^t`Q(Vj6C-L4@xobZmh3{C#1b;Sc9&4JSQsE!EmrH+#?|Hi+>3gpLjH zezbcntL{#9qV&AArPsc?*@GsrL4@xobZme=!@*(R=VfnuHQYmu3TTWk>F`w1Nz zpqCNqtIGNe(%aU$Py6a-51PaV5x$?$u>oq)W5Yb$@7HKejefND)y*C>i47urKcRDd z*4Gxso_LdIerusO-U*oc@ZA;%Z)YjRBLLGM=Q8BB&}@UHcsE2Qf};!-OUeBV*fRmf zH@GRJJfm0=>DCBY&F~J?e_aohx5m#kcFWZBgEl8+14|rXkZ8PI5F4f4*ZrKG><{;>@978^Ys^YhkZGdKZ zi3g-~Om6iGh)&t9xRwFktxI61v;n%b2YHDH*OqJpG|PA60lKUonei3Z`fLL<%Xe*n zF6((_e8sI7+W?*9efS&x`38i}?^>G(x|<)|3PY;f)~bHB0itYcL3eGSR*NI-R1cs+^?1gCE>f1!G9G>I8jin$L& zG53l%@w!^F5v1iQZSV>o=w!o3AP_9Y>yC2>1pW&B}*&Vr;W&<>+p5sLBv7B<5u*l&kuj2XH&N zE0Fd9AR^_eekeve#kU4~r zkPk05P~v!}(_8{;!q^p}drItl1HSK+5;+AiGO5?xJCk?d4ih4!J7vp#L`=sh#BwWf z0pT3nb=ymXn6s3+ZC@}5kqVEGpCPTFmx=Dq8YaS7tF(W&w1=3h?)d2?KbT;ta8@eq z#ueGH8@Hhw1oA#zrc2_19gl0Sd+sfc;bGIcJO8_vFN3_JLbVqLpc}5+5&l zj59o0Ej5OsOjB}RosawfSJ9UBYB`hh==OIcup3P|N1_9O!ciqbFJ-ba{m|!Wm zB~;<@@iU|q`*2#po=VzhNyx51J0HWeNy}efv6So=w7UU@3HD(koJt$%T~DGp+|X3f z4NbWuvJ~9%)=nn$t+iG{tmL#R+7H%?5K_(H0~q(D+WicOlkRA>+y3&t*H}t8t>Eqw zXD(jaT=^`WjY1CAEfa9ZSm;L#6%!mP5EPHV`9^m3+FgSjTTTVC5{PiNZ)7)rFRrDnFlPdzIdYqU9U z=Cgq^>f*^D&HwobwvIBUV_c(iOuWQ{rBIFsh=BSE??)-cbeOw6;>79m&eq+Z@Cj!O z@*CwG+YTodZ(4WAcIj?h_|z@j-I(+F^ZwM;b6!?IhVeb!Y{a?7`GN1dZxqkObLm5> zcZuK==6dw)WO`u)?szUwwH7(p;C|{-is^XLyRoz3AY8m*-g@^Xk*r85X>elu2s4|PddGoY53j{qI!EU8{`?svN^ ztCptQjk%ADK1$gqlDOOBD-dP>ifNOMty)nhqQ_Czk4ETz&i?%9Cxp}!pq6Xgr-Hqd z=Uy%O2JzrtUzootdiq0Y1)9@Jb=#+}xE~F@-4Tn)OZ$QGQrYunO^_D?qqve_O_A`* zmE%F^<*|XJRXSxF-&!6`z}+Q=MvxrB!BABg6~$Zq^4tRKN%+&b)EFv^k@Zxg^?gdB ze|)YdGOccBO`1yj=^GxOV;-hwW+Gv}VJXZ&%}4OKp9#!_rLvoCFu_uo(Gw!g2*+79 z_{2NN@?928m%p2VSwNlHwEH<&)9}n4>*YA5D8xVI>>$#O*APT-C?0GBZXx*hKl4b{B8i_-Bfw~11Z|GHW5p@*pfWgv(shz za9PM<@=`NMy4zhAOiN1JZ4!B2v?^6Ya|o<4L|&F&pzy=*TNUQ!o6ApQ5m+h4{Af;nxvo|LmA=dKftbys}y-~e3 zgs9jC>jfQE$5^WkzHfy`5Fn`RMjP-c(+8TmHu&BcwgIAJqhW$?wqZKoW);^tA*HuV z*{(T!-?0&I7qeS#IIUn?;=wn*Ll@g%ce3F%1$=|sg~|~mGzl?u3l!gf#J3!&_GF`C zAHHcwzSnhbmX}f@dTq6jrI==>Bv?w~XKRIF7ZcsB&U)D%+}*SSQKprAkG3}5O(lIF z&${5|+r=mrJ^(R4Y`iRaU?btOT!GTNsVs&ZbI_31xjy)mH@pqnDCPj9^x3R|ydTz+ zB`YBwtc#`O-JCq1$Wm~#1t}%a%lFR3k#L&b=@J<0>m5tBYRThhW3>H%56|hcjZP`4 zaED>}n^B?l8F%OUbPfQ&oD^HmB*njpBw-5k3=@8-DeN%Kztm;1(7)v`H9;zrWBUED z%y=n@?8CDy%}Ra`GWHcQ55vEM#=m{mO)J>o6y@JR%VCNRy^qXR*-?&5dU#e1 zA>>&jQ9U$A#`9uu3lVNG>z%*iIV-q@Nc*YdsEFsqF#E-`YC_;C-zcU-y)w2D667TZ z*$2NxSk_&U@XGlLdij>ccxzpYgy;52I^bMrSU!d&`mDp%tLu)nb`_3VJvQbIxV|`rEWt>zt z5slQGHXdNT&_pFM*ztu8jx98?1&IfI%HJtwd(7x>1KNm1S{ZYf1mBFuQgG{C#Ws?O zHbNZaDSxdQA)F_zWcOzZNgP3@-=^-`px-6)VLX_n;EwYe>4teqT?tha#(?>EwK=v- z#|Wg4k_e;8^o!u-36+vyDf}Y%0O2LA5UMhLph+yyFO7TXEr0(UL^tLj%3osVR4C&S z_E4u^elLF?zL$vc08z#x?99%;Q(xMEPx(9dy*2{sD-h*aE$rDIzk4^m->b9%pStaw zoD3`OdZMx*DiEa&&@3Q^7WV!UQ=xnnElbND;=q$V@IVqH8|gC>?28wc&aZv5S; zxi&y_^L)>>*N^SoL`cybD%KRO`0yG2`3gSHL2sD~kw_n9sKlOykZnidpkHmk$2qpg zso>nLUs<>Lku>*;m9jVJ{t{fTE~#&#Ey=(T&p*=z%5e$2M3}E&;S4c literal 0 HcmV?d00001 diff --git a/resources/profiles/Anker/M5-texture.svg b/resources/profiles/Anker/M5-texture.svg new file mode 100644 index 0000000000..8b04342e3f --- /dev/null +++ b/resources/profiles/Anker/M5-texture.svg @@ -0,0 +1,1003 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/profiles/Anker/M5_thumbnail.png b/resources/profiles/Anker/M5_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..e137319201a97ffd20f0b60af13aa928900f02d3 GIT binary patch literal 37200 zcmb5V1yr0(vo1RLAi-S%3{G%|;0*2(oZ#;6Zi9PphY&otyGw!v2yVfHTkt#i{=N5q z_TA^4yY8&TV&1Oq>Z+%!s;lemiBeILeuF}c0ssKs$jX4#p#K{I02lxe5&DgIOMe#t zKq$A?(00>SR1h$Av}Z9kb2Kq$@v?V<(g6TL5iciWQ(JR43KMfnYX>3FSw|0u!rDv- zq{XesrsyPLZe=aw<6^GvqoiT#V{6K91`-iQ5%dy(GO#yyGp6vew{vh6@Dc+3!&daYPXDFAf1BdJ$p2#?6u$rc5HDk=|6ONA#s7PEd;9;UhO3*T2Na0^0QJAg^q&*B zYIr-Dv#Oc9I=Z`Um zm${t~i1XhZ{t9X5{&&a+2WzNW9RDV1|C=TR;^kmv|2IkJe@ptii~|2mp$)dSH+T4# zhW{z>zo|O^3-v$s{D&&xXy@pn4s{xHsQldAoZSD+_+KjxMGi_=bTosK|E0peNwN|W zDlU!|)^<>uv@FyuOx&&Q+?cH$C|uQ~#sA?0wF?30U~3C+u(2Do8wVTI{Mq=J**P`X zI0U#k1vuH5*|-GQ{)O*9y`gBD8M_((fA#ob^8>`v;PLEA>Bw|CiSPOKgIU`&Sz@=0Sre>wiU0=)-@- zUvmd&&~<@E*Mo0(VbBO3V-2;I|7j%v0DY2wl90@RDCv-)x+DW0)_Vvae3cSDIEGIS zFUF{_5l`2ht?e2$i;J3pB|gNquP~-~98yH#a08U*B-|Zi$ zU(&+)fi6hr-o$u?dRffP@}J>EYRjrYrlRQ3+9@um7R}Q)_z1}Gy(~sYOZK8922JWh zchN0qBxvHXBk?{I`%xa(_j|`Q=$6+l2;HNGdfU@{bI+oJm?9%nQ;JKyG1oAn6t9$u zj>0_|5quhm7m;Pj)Hi2yVJbkuOWr|x4c%DF3O{7vWgvuWH?(^+MLSqR$Z}tK5pF)& zL*5KTtZ8iSSq4~$$kw(ronuY+ywTTh;r7O8U4@Cra~n$}9aC1P#|^sUeE3Yt=7kEs zu)41M$mb8>_%VO^#>oj7OTgf}wzJli+x6`Gd42<9#MEVMwJBK|x?v(a$>_KO0H`>B z|6l-FIfMWJ1wa-ors0+IyURO+RLh&?C179ULv4dK*D}FGJyWfhA$tZUoe}n^)alh% zet1|uW7!~iYzn^B32x^aoMiIhI#aYhOz~DxSZXGi_dL)~_AbK2GSgUclYGOC+oH?& zyXP0a5A#A70v#0nxov*@!lSC$kH? zbDkCEprmsZJ+lnPAKKcUft*?C*oL#K)dly0Pz&$I>vf0ND>ZUTy^l`Dp^QV9HqNfj zfn;gOerj(WOZhP7iMj4c;5D`5>i5Yt0zK0*C?#T=b0+g98Fl6=n6*Qs3Z~KXCw_?8 zPBF0Om4^Db8}@5vc1o_0fLR`;5=xrsXT*Zo(}5AjGV;DQ zR`D&#TDY4U*s~Sna`KF421Rh>q-z*X@-*j##h?97?9RJGS&==1W7XE9rh)?CJU52N zM!-)4=Jhx$_`O44thHC~b8R%Cudo%XX@Lr2ST78g#a=hI1zH9$$Zxz}X#pGq??@xj zGxss+NDD20TXx?nZbtMvJZ=rz^C5ot)Nl9<3kb?;fu9M@&u`6*Gj0)v?QkVQN$-SJ zi9|4g6d-*nbh?M$i%H&9G)iM z<^t&KVsa8UlhXCO*5`f9I2dgYGzSj@385h%07bwhA=t2z@R@PhGN&nXMhU$E+?8%)JuOE-)U4WkoOVg`AghMoZN$C;Vki>wyOZM+?ckeH>OD z6GZGmY}z3vZr`$9aazF~Fu`@Na)Fkekrfbdw3*b&s6dq@EV1T{fx4M+x# zFUPl|f~TTr9mmM$!HOpV?vayIaPfL9BNr5$fFN8SVK0p9qBI;~o9xLsX($aRUOwbp zKQS}%ma6@AZtm5^IP5Oax_^a??+OoH)@agPV zCY^xU3xD60+(8>fgHU7H{Y5=DCsk89)q~jFyFvmcq{DtJj|?mUGhuYEb)XQPz!mS= z;HY#UnZ=6ww{I3QN9XJBDQOV_Uu;-I2)ev=AITBOILPS)SwF_Nfn5K5OMv|x{HJUq zBJkmZN*5deFu#WJI$4P&@?bzgWPA3$2@Fv39ZKhMQg1`Euqim!{nN zl@^|$8>M17`?LdaLG@aDq7GdKI^4aSY0mGK0YfWB&5mR00;SC@&#bw4Z7S{!{uuVBd=vxdzeR86Ip`g5YJL}ytnv6ljztI_uE9$S4L$q&~Kok=i6 zDnH3PJ>*IFkP~^NcP8pJWk9yLI>r83@?b~IqH8)$F7xD={gSsfC&sSTV}|ld7SjA7 zen-}Vot=228_E$qKDwFu`hwQ=L_IL#S5$4&^GyRZ>d~>sGZtDVbC*3RhkB0d>}(tx zIQ{rBiJtjBgE{`7Ioo;vsm@=9r%J-9ulASFcIU3GSXUW^_vQPHNrMIGrsf*VfGl+U z{hn&7N%3eYS?4AfrGh0#@oW!0Gf;_*p>V%q$?JVd9x8`t*D(uj?UVhWvLGK06q_g4 zFyyejV?5miu+7zPbyslxMSq9M#>t`X4Jh5{hUp_p z7r#Ll-Nqb?ATNGt>Ona3wi|z@P5E%e$1;%FuR;UKXsluB&s%Y5%)PxNU^AofcZ z>LpKOP0BM0Zta7C%IhJUav=HFi!gt>lpj8QmsIW79B!kleKQ5Gzh}E0o4cAWRek0o zRsFx^@3)`9Cx*2Ab^{U^@vMm)(#|;_?i&8x{_RCymrDJ~9c<-m!;bVs;*sgYMEpTNYYcy*;N9M>#Ri{kM4=* z)2)i;cC5r3tzGws~jY-N>1-L7vGzm8tI;YelfGK%{nkAfLkWh{AL(2ezpPg8Y?dv z&Vd;T;e7&b1#Td8+ypy6nwL1ZAF9@&L4ytYG7_2JV?`88xDL~_`a|4n_-t+G)+mWi zXx(co*~}Vw_W8_Kz|r3|M5q10FW2Kd@W-42{Z#6DqlN6o=*7Dybe6mJ{X>flM%soS zQfEVeeZO?|5i}lm3b@(*zlgh z>&!=Hhr-MYXrk2GK;u2FHS>+ zS;b&or4AdG6hjRe|}o3rk2ae%p{_qz&-Q(vx}*>S>Nw1{`!UNj7M?im`twm_4n5$ zXsSB*QQ-R=mdJJONuD?~(R`nQJ!iAPhgU9N>$lG*;XKYt!iO`@yOHxsCi2qj*1iWv z#^LFNtMHaVa9JtkzPv>^YMHJkdseynK}m9vd;>vfW7h(joIpcY4<}{cA@Cs4JL}JM zq_9FXZuAgn{WVx5_geV%WaCB1-TlMGLd#2Eqqh^tL@EsNv-A>++wZ6aWwrdPJ7$rg_cBQ&zN@Wm)y1HiP=0>{9 z>~p}h@l>|ebzo{#0_N?scUnJueSM^q-k&r+tkj6KMG2XT!<{7g)Z{;5-3IQ{Vm0^Tl;QVd`ohkKm)1cYc$Ltje{PZMCm|sSnP8!Oho?WW=PuiAbua zuKY!`Tdh>>*E{dstGPL|2Y*uIW8DfVv+6c}5$*|Gr`))^edrEU4h($7IeVPDQ^=F^ z#x^>#>)=T|{83$UtCbsi^j4H)5 zndl!89FgHcL82MR0KMa>d=c`zGL2+vV=6m+leo+TLo;zW^~8tNhU|Rhlu3-iiHp() zV$73Z&?Rmm7at!u@S?D1aJE$bB66|k17Ne(@r7i$#Zlz>S6RZg^7_?B#x%KU-5B$Td(c}k^2-+r3W^@?S+*unw5Qb{tzI_5U9PQ z{oHr4>!nphaLB|w5BS~1>~mvJbO=x)gOZ6JABB)GBjNt)(fubxp64W2oEm^y#eMD% zl^zUcg7F7ola5Seaa)v1DD(ZK7Py%l^%H>aUGM5Jj=a25r({B79+RIa$Y5_d3Nsii02(FaD=-QI)U(Zmz*E3bzy|@aj44C3ErNk`!`jW7 zK0dxBWgU9sY0*qNo!S*?6_eS#%IlX48&hXvBC9?~I<}M`uSN3LBtUP+`u)eQfai#G z$eHKE|u)Ilb4rO zdeg1@>)l?DkC!HxFFr$E9BxNe2!|uEt&S&~pw4sSt+uJ1r{`40tso*rKMxjG1SSJW zQjP&L7_!OtYZM++JFE{DFldsQ2?Gl((QmMkeMO$ldoK>JHva6uJTl4CY-b7tmEz&h zn|wk9s3!F@{gukF{O5TJeueTsV_a<1^6;&tEW&o*ETi{anV8;9td?oa=D6vH0E%4bRmg>31OsZsK*@ty)tIa)-oJT0t zRH9`V5ULv=%ez2WVi`D)!3EtppafBM!cBZU`a4q-+z{;OaO@Cqz|>VD82P6_+L4tJ zmdve5aN-{%^xt#}rr$bnkX@CmC}?grUZhqGM9&gFZN1xCemb+`l7yc`v#lO4`!HgP zO>^H2AnR^Mgn1sig_FAeV`t zETA=iX&{AMonrGk83>EbyGw5&l|0A>g5XHpNEyVgJ=z}!c&6L23pC09`M*i$}|&pDYR^T9c#8wV-~)-<+5 zEK#5HTYL?*I{xt4>I0qP<;vih9D?mQUCYhj30*f8KQo=*f}c%Jk;Lf*KQ;%^6tfx* zH6~^T41RAtf*vAr0fd6-t8DBv@t|y&xC(sIz6EoU+aztIEZP^9(VFxyenKMaTHROh zU@ZQ6&bz0cleaI>B$z>DiW<^{7*B>mB+msTPOT(HFMn9AU%O`Odd}f-sj}Qt2#^;r zE!{vphHvL{&Laxrq2Z2^ZGrI$X>=O-AeR*a8>AMUIYdH6b|Q0dxIOV6*3ZrqOa9XX z{ikROxf>O_VG7VWYA|K42_ro`Jg8JQ7ywEuCr*7qHgiN3 zEl19bn$Jne{Nj;qdA9e!uV^IIzBPC#sY}CCnvEd^I}Yv>a9`UMV`>Y;>4R-(23Tt~Ds_GG~xqkAnrcDhvjLL7-G`Q?Sc3!9px1lXv$)XffP2ZMiio#HwHF;lnqd0N#_ITfVM1p~q3htnVvW)fS4 zoWVLrW>4T6%UekL&M_YbMHvR;$-o_4q8bqP`;Dq86H2(6gy=^tt*J%n2*e(vxbT=G z8-(C^#osX7oPz|t1kG4N`>kitTs-i_7=+??4}z@bV4(m#q_W>Rk|oeHU|GD;i&(H#WrWh8H zh}UElzzU=`Srsm+Qzs0s%TS82-G-Ei$6zI?%V32NLfab(QigepYHJ!xE8f=8@fbdD zvp6)QR)>r0SB)zs5!#r*C#zD+`_-rq)cI$KwQne0KNb!N!w6dZl@XE#DT$wLw5UT8 z1uq71RIx`MCrW2Cn*7w-e+TU+P3LdeUGzmtJDs+1GdUl^V_I&t5+x%ro$l`G(@s>S zs%3>2sVnIOcL;N~m?2IR-+IjS@LcTizEb)hskeOl%A_D_IET^hV4et0Qrx< zMVd|n8%^VUr$0U)E%p*;!;2M!6acHasz%DC>H7|}-}IxsgN+y`d+ZNk!bG6QL`7@J zpq5Rhj>1j}TgVvH^_`VFFgMyH!H1YbICr=K zdW`r9H|2R;GTSNutO=%JrwY_}^pYxa9at8!hUM?FhVEG>;SZw?NFipM1=(>VWdC~EVaiC1VM4&7ndIQ(^0r=NrG`*oVq0Ku zO{ymafK|%pMu-Afv{MnR>+KtWh2|4X3mjq0dOQpD>~!`4uXXDD-RV`mTEBIGjq2|N zSpi8QiQ@+K-VhR*Ir>#)=Xk3SQ7kMbJ)+9Rg!%G3KTHHs2EbeRaRe_QIC;ORm8fng zeE-s<9@#Vrq{&qH}OnGRRJOEo2&Ve&#~ks8sBuD~8p^FtckSDvCo@ zZ|wuuQgU1ppe=lh)s}+v9UlwdIhv?+opRg-Fq?te0cHW@>}tcPco)b3W#qNd;O(k+ z`IMZJ_h?}F5E(~``-uLmjxzo#&O+MAiW1)01*4YSQ^}i}*7e)9>1LYDR~w4bOX`H= z%TIns2#Q4=Le1*$lT^&6%9g}rz-WeDAdD))G$vH~^F0N@Rqi1FkFB%gmW#@x7|avZDj!+N?^|zz*+63;X&~H1{)b#6<{M{owSpyF!EU`Eq6BMVj?pnw*wa@^^J;(D#6{|y_yQ7 zTkmFFf7M2Y2TILgKJoJK-7yq?Wu>arswFe1GZ~uhx(o2!7T5;PV+83ct z*I?XuIe(tB$CG(?f+ac`%NH+cw?YJz82HKS(bbp8`wQ?p9DZgWPO+#drg*Sy*Q-l%2aA(#T_kvg$kM=jf) zLak#P%bx&Q(6(j$eRAyU=jLXnOiDKpE^b_+LVtdK?zA;s2saT$@8vOa>%X5LI1}$W zC1iQ-n%`mjeW}kbw;3iYLlSKLCA!|1?Y<<5zW^7K3VI_-WQsWwHBrYe%dRdS_|Mxi zzkxH=>Bc+KBnD-w?-A|RXw85AjJ$%?TWtNQ=_ktRil{SvgLwmAo_0H+smL(}@DqnC zA$;Ek*35wJaiRCs-aKpjfk{Wev--<=mv6d`?NsgJF)PQgzR!7Y@Z9+B)YE@o8n?il$C}SJMX>2md@P=xcsMIC$1BT3ZO6LML+y9rZY%W zA63$@or&UUW!0!Ie+?=Cb3I|cB@e(frrL9r)^WnPKNPw&P5&M*8({Oh>hG4!&hxpnoD#hGH3hJ|p_PybHHF&oi*YelUYqB} zdV|A3=*7l*|V0ZboKizx=9izOwH<@^1KDEs!|0ZZ(_KdGSI%VUQ8~4VY`` zYYII5fv#5kHs2~?c-(NI1kz$&V4q|-|NDE*>5_r`-di_|6^V z*h5So@j#38P+R_QK`@u%nk9WFpJC`twpM){#LW~Ggg;kdy6J;eWo+Y4^V%q{che1P z?tU*_fdE>td>V{t4qm_OMs8ait_n$n!7I)7Bwyxj2yjutyGqp#$0Q=r-*jEXIi|XP z=a<&3{rGGQd$h33$t&D-5i-`jzu6ahqigS`M=D8U@llYDE`J~{Z}i9gN}Dg2q~;6v z)^0OC!Kw-MFomqr`mYiXWFhN@3nteIYcy*Ug_uW{D~(1ov4jRJjPx0CMH02?A`eWf zn)(wS`{hn;@JC57&BooCZnNFe=Yf{C8=RI~BGUzYlyEqxFAX$I3BUj3e|y~Xa(wwK zML8Z(>|9wkxr%2v$8`v`HlvYz73p<5qN{27K&E&`+vgiMYj(raB9n$tcSN0gsKy## z(*~)G58eE2!jsaky9+bP_hEVUt^sZO9^85G<8b9Ul6O|gPq6(OTh*)fNL;x~?e*EM$7XlQ7ERB=C=$F^xR+ypH=yo|i{waXE4y4W82U?Dtz)%m5i)^X>1?t79H zGI`r)%%D+S-|>ZNJ-U;j^X}LCw==r-Z!729?@l&EDsPU5sU}hiKN~Dw>PYR;mS?xL zm8!IOSH(WC+-THa&!u2VzrUhlfm5Z&-$dvxYb0Kr?zisoo+ME+#ZLrjv9q6|bCm{< z#}cNIaO$K`MsF|q^2?Q~<+JbIfE(q(rv_~);M$Xz8k7dgacc*JqHxJsQlde)$wr?`xel3tR5eHVYbd1d( zFzpQB!L^ZyTL(a(yU|nrl+2duBA0WuwveLk;n;v`@^B@GKePKuX|bSEyV*SBefMDp zNJUOoS=sPWiyI*#GSUzlISZQt?2L>yrH>b?Ny%*@}aooj6jcniPb{V1H~ED(P%`=cUTgq{_P< ztNIAD;}x#x7DT3os4+IBWsA2jeUVRWflv8iAE~hD;oy(hYoeEh*qlQetp6UhKe=?m z!Y2+8gv#3a^*!2v2qP{ENt)UQ)Du?t4m*tcLWo3#y0S*)-z9B0^BX*57M3?`qpsAZ(uO#RDaYBt!BWN$r z&?Qtj1%%FfM~AoO&2rYAKxhs+eR4Q(WUJqm}2 z!-zRu-U^E3OwZ{F#1YBWmru;g$SiZJYwIGeB?(P@bGPTA)18ZSZz1*^_>6+t82WhH z;i!l!*;C%MZpeiH`n(lM{(=!&hKc`Fc<%!A|U zYABE>Zf*Hit8*;b1N|E{hwE}zna)rC_@6t6Np;#|XukQK`$A_QLPJAq@BB9~XH55V zT)!*piu635`hNLH{=P|t-lbe){U((BseqZTs>5m);8lV9T@yl~D)%V8QbT68A$7Q# zfz^1}XQjCLO$}QkrPb<1CH^M}v(9dakc!SV$xiBkh{cBg`)g>GZhw6+7OF)y?QHC^ zSdRJ3Zmn}NA@jY0Qe*_A_h;stO9M~;Jq3$yQFx#APVfBov+l=XvI9j&pE>cVE5t^% zj+(ApJX3YDeWzAAEY;+qxD(TRZFd%E8{rM;qS_)1vsKk8Jp;rbMq9~7vk3l1SDOkkOY5nk)cq7N zycR0^w{;2r3iHP|hI6`}SX+69&-3YVdUN;TS|9NUO_i^N0yV%MtIi`Q_R4V9DzatO zlkx2v=jSR#kWSyz`?JhrB?jU+#BDx~NjtzIJO?ij-~rk23t*${-clc~IYLQ53N;(;m|Nc%mAa0ZC_s|hVE!<& z+p+UvxIyxlxGjGMZ|&=XJZl%%Ytin6*7P28_9DDkpP~Qq(arn(6?W#jR4onEWvuCg zlj9g-C3ov5 zB8ShgDIfwTJOo>21=6y?IGE;wsyZG$?Udd@L_4&fKR>Hw6n!Aa#j>idbJ-oY&Rwba zP$_rg^GuATv-`MC5E&VH%W8P4A6V-(^e&a%0y!zm(Z2pB&-XNsBZz~BR3|mDHLp-Q zST;FnW@e`UuIF{Lsu({_4q?bd_FXkzY4Xxo%mSN745j$@nmV3bqU!J_#9?B-u5j%z zns}<%*x2dwb1Z{%=(})*gM>a_?_Oi8SMdhwq?EFN(}+bb6z+c*7R zg6S)$qQ$|g5X~-DI@bnF-0m~^wjbRi$L_VhWMq6!n+jNMS&&<3?c5NWGLJPaetg%m z=0Q@e*HS|~a={@Qk?Fm!P}9?1p>>zv@x^zS+qS~;B>{D>>$sAf52WfZ@G|oAo z!VC4NQxwh~v|hF98%xyie%B>S-gPfi%IKmrU0aqkIlUGKe+#Jvi&s>{=%L{8%!wMl z>J|;gJZ?4edq;qJbaRts4tqN^jigY*Chsk|r#uOni)AgUQu)GtmpPmwKqlF8o+yv{ z&CM!X&K8RnI7n!kLY+DNV?STyPBMB2-kDSa9#TX|TSt$NN}m>eaC9nOuF?C>@W$nS zk@bnh{L%C7*65|z&#IadbS{YDfG1pr(Y5(|ZeI|!g87r(>es>*?H8QVu1dkFtEL5L z<%r}{4O?`(w3Ji_FmG{jF=0+wgiNdF&(J|WFZr$<0ti|>h!7hTwQ+;A4h2O>MO^$y zQ8t0@($E(%UKjH{VJ@YDEL2m>&I{dzCe=bKgjdIT`_NGM`l8kI;AvYMa8r5M68PAn zK^RflxbR`)t_7AuAVmx=Nc6F*{fpM8_UIBJT^hLxE6VF{o-cuomRAn+v|+}ONb&%_ z=$%1~Vy}ffbb{gW&eBNR_vOA2mk%Vc`+7>reB7gCnQ5ROHryOhGrzMsY`@~ZE1eCw zn(6_wH&`+szdk1=e32?H+~Kg^t_3q&C_rQWwXcqeC$9yFcx!I97qoo3ds?;W z@uG)J7}#ZJqH%Wyzfj8pDVRPs;-(vV&_J_Qgi@~IBU39baWn4{7L16?53t32eR*9- z=X3d01K)F3>g?`a^!>(43Kyf)#y<>S_%#hevaICA$>#c_48}ZVtq(U=JYDN z%_sSVUQGRDflj`Q_PiUnp!!C&cdw$Qv+j<}#BXPUZ>6QA+l3$MUVRVX&hF=kHo&%X z4xfl|eq&H@;^ew%mM;zvhx&BoRgm!Tj`rNgInEr6I^9ecN|X&d`}zO0(&rTv#4SCh zN!HX&T~H~J_3bvXs1&M|(j}w+Bq!FUdr|b2U8)2&$frPqB8UTeABRK%-n?2?OUPU{ z-C_`$aoq{O{Mj*VeJEvp?*DA*0k%~#5T+|}h?&;4Rg)t?6=R@Jf43{ww2U{BB*Mj! z{LPBxmm=)OB0YW#0Qu-m+U+J_6$Q0TG1#UjOQW4 z*_{NDa`d;PCBCO^=|%K~gS05NFJ>&H%$k$AGoj*gBv zwTH35`}C=^o`<8#72+J_fJ>$J=UZh~8?OF4hEhXV$|0tJe&q0D;(2nRC(*$EDAo{| zp`#wg@FSItC+|>$H6eW+ZD#r}VhH9sKlNmeIIx73DQYhSMSeE;_Q++E69F#}J^YXg6=WR;a^+_3M zrK07qbMyGHd}_Kg9odin$-Q5#lVqj#uINrrUc{2%D}zO9UY?#%7**!I2K2!)IE2!V)EROx!- zfAqPTP+>{Wj^eRaD$$Fj?qUCw*`y}N-CzFb()qNUUWo)v1iXz2Ul_2+aFfVyZ*GKe zBDU^jlW`jtkf61;r^(`k7q=<{4!GXUO-#o5qb%{dC*L7)*%X(qAOxoOojAbEYG=%ZV5nzOTJ?8!1Z>KYe5;h=Z)#A7! zN71x$6>>vWOqDM-o@7!dv+(t{aY1L;;H`e4a}fnb97zfViUe0b(DIw9r0(d=Sd@ciUyDnlg>H2Jgho`IBb z33A0Eh_P#fWmHgBMlgR58{JTj%>u_7N1QxC;tr#OZ*0LKJ9|0J>%3&1ju&t~jqyEx z=MP5X0(a?;2(|KSFkwWQb@d(2%xzv?-gAQR!<@A*=h2P8_%3$pWzqc=!8JaLSf2qo z721f*yEwJBBA>^-do9D~J%X{6)Kx+hA7}JLU|bsPR@$T_#r~3n1yupDiyXLtxB- z=uAo#z5SzUImo@b@QPqp8Fuz-n_O|CwQSI=%JMhNY+n1`^zQ3gAW0k---3oW$C{97 zn4JF>0D3QA>a%U&3GXwXtRHB0;WFogXp5xPEgCnYgjI%5qw8Z$|H0;0-9{%$(HabC zp0T+6-$B$-Gcq2=_x0wj=Q-)c9kV-bV1G<825}SpkAi*QVw`b}_v0;R2uVWO7tqKA zdI7xsQe6*Q7_US1L-L=pOeoK{`7Y9mOmHPp^S!pC@IWr{3qGO@P0*5sbmJ{g)TmhX z&8YjL0l}KvoFXR&pYYQb$*DtnuggFqDGvz?+Yz4lfnn1kjOQClGL&A0+hv2 zw6Ae~ECWyaJ?faS&iZ8xQbW`O-AL)I^AzPD$!(H#G&p;AuiZ|ti*QTt`4HoV=_AnJ zd}csLQyNkl-gRz@(=FV5)Nc1;k1oX?K6-N!t9&AK+YslrPTG304B|on-RQ>sY>>n2 zjnk#dW>4}|nV3h{8XxF4;!&vx_V!0rVbmyO2wDgUgZ)Y-5fhDuCHBM0XeZ(Zy0>BT z5YLRBcS|WNn+M`95TO1FDNy;sP;FoLr5Kvv(-apM?{m6}U@>0{o*9E4TIi$EZ}09B z9G2>bV0#sL*SL22?x!PPl1~FSDF>Zb3EwjQNan*m@iq@~v%{vAg_psLhH#~piH@Gs zxebrwPr03O54_eeW*21X-~#FcXkZa>8SsKeaYQpqtU0}PC2Xs48R@;!9%qK<|Ayf48TTNJVR2E+nGBnWpP!%2Xc$CVd^eup9uX^dZH(nh_1o??yStz-Eltz2 z2lWT+78y%HfX^!Qe(hyt`x~C8t2#(Q%zDGS6yFG;z!4G){^BN%dwKS~)1JCZacLW; zn577>Iem+kfL0h7&;!X<{-6Z0)2??O-x{WLq;zUsQLMKIs>oqMG8yDzt(5{aMOUvy z-`9V^w_Z+-f&NYu*s6m!h~{yd{XX4KPu6lRb+RIDQl}8H)Tg;JV(k5poNKPEr%v_a z&|wBWgbkPB&$y>ndFpI8ujP`8Jyv^R}9`awVBiVSwlEK%ju2XnvCIW6wXB`oUk?VNe;=!X|_I;CN zOHRGlCFX5zI3MrMHdZT4zau!bAJXdfyjYtr^=hkE!W-m?($gb0t71IUOc0o^G@n@9 zU*bt+Xly4#Vn)r~Xb*vmT{ArCA@M(=VKh^x)R#Kbj_sDHya0Rgd1 z=lc1^WvnqI(rL1zehSC}uC8qFM>2rgOyF48FQdQK>zx^Z%)_6HKKUx>Fx@%9wnS_O zcb2+H`5+{k(9_cf$&$Y%&Ej7Mxv8dajeW8GT`FjE>tZFz>6IKushj_#k;5skZej^y zT8p<2A$yrK{-?JJ zsR)n!epK2Ds+-&SW}i4FZhJo)kFM>8_JFQ$GIb%T03U?lqxbyJ@w76j1i{OJ=%&d` zTv)JG4#ciVheDyvu-kgRDXc2I05|Ej)1^>FyErv3A$1zmX5}vY@&jnAc$DR&V4WO0 z+r!!5T;C>KYc)2i0IxM%>Vmw^bY?@otp-@5PWP$Lii@^LxOKBCWE#oDhPn8+{gSudw(k2T&!Ge2wDi7CajEs9x#5k7S=g#6ns5k<-F=)*6ViL zPt)pl`c*TvN;5Dp;C0urA*8jFxbr#Np~!xvxg45D-*6M(XxUT;j@|jcDV5QmPr(Zk zsD?CT|o-KM!Dz=L6z8=%+0C-@sSvIDPC==x%|#Bt?mO&($RjiivDfZK`p;G zl4-Nz9{w=miyF-xQHnR)$;_1~pP71mtOy^y_a1NcORDO$t)P)akHj%3CwNEIm&W)@?aN9} z_)3e&@rQ~olJETcv1O+(wP&NB@k!cdvv>aR}6<$IRPFT)q?&|!_jeMJxfEW|}uFop9A+Zqi;(8C5!W7>r}OWLd-x}?}974XUL!9|J;rf3si*rRw$@V(NI%BeNl#v`@y5Dlcpjf4!ISY!t$t04}>k-zram!LCxZzPkU=E5l|+rF6`kz>{_S_a7fbl^qUrI?>r;{ z-V}|U6G2SHB&kA^k%)+lnI44L79@QnujCrA)>gVqXCAUtjY9PaBFKxr?WRd2joe~C zamUNO`5B)2&S3EKVVp{dF51yTajCD_O~^uNG>;{6Fe>fm@4RE5l)k^ANT>RSKTNDO zZ^C5vk#=!b)dPJ`-iGnkmFH2Zg;XX>*VqGnXL(t1HJBq##8nAd6d!hM%>y0Ylxdq! zig3}msy8Dnr*CK^XBt`$qB>dcdM~e(VB18@D=pf}8_r1v24G<~8)5r=v#nJxtJRT* z7MJGYd_v*Bg5~rZze!%n*!H@hfI=a5@2Cjn?X-Kuetlym+9U7 z2j>2GYU>E&F{vV!DritByjMJ7V0^WK1`Un3$?b=t6cg2Tf z&gil~eiGWzv7P1aMFugM#BL2@>X~v1rNB>@$RwfSyu(+enE9mG+uz+3<*2w#BU`mI z`pm_n3f<*zw7~>1by_T%5XdOxLo_5-k*oCfEvFvAlojK1p60?8mqbRzd~&M|S&Ul2 zmNiD5G5tHB1RO|;aADJ096J)S10T)BBv`i3SSqxkckKI;50Rk{GRMb@Q0-AO6~mSM zp+KZaW(vw!6d}jl9HVx>OTp#sMt_WMB8%2ARLAtsQ&^-{r;vKjq| zC{wU%hJ}<|^LHRetUKr>7#Kn}D5mrPW0-<1!vPfsx zPmZmU{YHX`WnGHPklrQn+iqCSq!lcOCv968T@1{_AB;j^cw_9Mri{cU)rX<)+E}pJ zhmj-1zaUdzry5M|_86e@b(j!~y*$0uEcV7Ahy#zSjgjddRkVhnK|5n`SF{6Bw5B-u zmP;Eb+YcPT3^zd*uUTh{!hshnG76hZ60z(UjFa+VU29V1=3S)7A%?p8Ez zi_rD4dQ0N{6D)?s0JdXO75#Pxjb%xBm$5Ivs-gJ3wC(EZXGE= z5tJs>qeGc4O%V8vdgvu4fl6IM6g2>+;iTRQ8Mpd$9v~Dz#5xtnEg=G9Euttw6h%m) z5XMAlT4{trvFMD##0AfoT|Hu<5TrIRko75?88We+r$MwTu(L*6gg$g=5NwTV&J8Wr zwQA-d04*-0%w?5&X7&D8!+KR{IuBOWK;^Mqb;+K7&(~PKd>MLsdh@-r!wLXq&SxlL zC&{=aN~Kb#gKMaO*2EdI)*@?jm85yWaU8mq3h5NwXpA8yg1~itYK5~OBv+**C5=*w zRQf6fVjdGN%s+COW&mzc9`3X;PN>ZO#pG+wQ1^>kzg zUQ;E_DuJwxU4ng_pb>`9s*cP^Cs2C5k$ZZP=94hMWWm-+bftZ_eHK{B9Lq<6?%2q6zBeX^6;o*@q2P4y<+lBLFh*dxmfOQCx0Aj^Ssl(*GID@LWG;iu7&9%0S zMJ+nI>%-=b!cH_fFRUv8fryZk;1r*puxwkrYigW&mwU?c_p&p~ z-fWhh<$1^eT1983nvD;?v;%g>Pp3S}T;2KDNiIMcK(+{wfOGZ2U7rD`FDJQe%b2J8 zut2msPn{Z?S(B#J)#jG`%=Cvg(K>W#TcUgv=L&5R+L(kym@v(>sAi6IdfPcodY7gj zHJ^M^Q#UpV8d>w1miTI}$5SR(Qk_0i#hhIz!Y-qjnT6Sk%gYwFHi5CuAz@5HaU4+` zC$LFEahytCb<)~-00_;O)18{-l$2nM%WD7tAOJ~3K~(-d zS$tR~7i`&zgRPlu>Gg>*M(XvkRK;=|rBGQ|+KsQY?B^69<~09VEUaaiRN4|ro_M4b z!Sggk3b59K6hV6)lq1`b}Hu%sFUh z&?cb`n1znq!!MnKTCUQC1zK(pQEnIzHJ{k*6?g%PK>@`=$?Yf3jbKb-EDMW(2}-%> zQuaioF8?%RN)+YK2RU_@MXY5eoAy5@=MM8oYatR+EYKpN*<r5j5v>k;EiPy)hAEa#227F|iHQ&=5sXO?$L=Qplu9KeNeqFj zA5=o z&)%CyTb7;mVZUz==iGCrH`UZtJxgk-CA9|B8nGo5M4U-x@REZG!bA{)Lzt&n7M2BG zHaIu|XF=ix2+XjO6@pk`X)OpyG7tm^iJ{e#TB;tatLjzN8}D$=+0&Ol_TJ~-S3|2> zRaUFiY^|qy{ocLzowLukzv1`&zVADK_8jK(1|&qHTx35M03Uo#2fUSwRt}FAVG4nn zxvuB+_V$IpCeZ)Of#(3cR#Fd!!#A%DhJSl79IgnV1SL1;@WwivvvA%*6$M74F%BO- zhNqr<3XeSUS=7xObv;9$D&Ol0u(P)V2bc*?W1|cP?UJI=?jvToOGLIU{ z`37Fx=VkBdiV47o>ZISNOTRuq$5jcmu_n3+K=OMpek{>(VD<;VNoEFx09=v?TD&Gi zX#)ee>G;if(;MFir4+35?Z^K3W0U*u|0}7r{)x%{-iM@=0Ra1uA~r_1wYfYfB;tFt z06EE?aH9OK4grYKs4<3hp%JW0J1kgf1t}$sS4MC0*8L)f5QJ1E zCvfceZ4d$=jwMamt%%bM7I!T!BhD8y%`z+#N%#OWZ?&fRjef6szmU=^t$DOE?5(YA zj5gK|efem-0vU@|Ap}7R#@^m8_Vy>J`hC3p?|vO#`?`Dad%yGhXj+5WY>LTriv7J^ zOs9L8Om@L6z^tIPM%%VsyRNFLAf>?m{x&9)U3gG(4`Zyr2PU0e^n@=X%$?lDXT}SRZ`LU1v;TNmg?)b?BcqTmk z^cfsFd=x7yYgk)bK{cqL3JqyAq|{Kd2T8NeAV?`uRTX-@9*77^#e3b@Si{=-2IjSe z52AA^7p~K@bvN>x34rGShIc;P{leR>i_`I}xE!2=vW_X2oQ)27z(MUDQ^LqZn2uFP zi?1BD3qjC@L{;^pKzk3Z3oDfr-a9cKkFFcEbG2)2ypHvaL$WIR`slHn!f?=!UI7RE z*`I#`TRS^IA{_|`$b_CVk`|M=ypI_cFxGN1Dyu1Cf`s7UT}u!_3duL$zL)}5r~;<7 zK7`4*(y-PuG z>I#*%k1}qap6v2m31&`Jwg|IkW@|g0IkGhq&jv>qqY#_AW+7yX2SS&q$#{MjVCKsF z_b1z-o&rTd^IzklgIo^f%u zUIHKidKQg1Ki9fSE{EuDq#IOo+5G#(KU?y+FL#fONE1z`z8k9`1<1!4h$7wav*|v1 zz1I51DuOnr8c~1vLNk4<_PsSBg#wY(M_8|{_GD2?(0Yrfnn&WZQ zr`YW?ekP@8Ha_#rF=Ed*{@uzIa?Z`uN)M_3pYd|2n@l9~vPN-#JCg znfo07uIQ@4NyHAYQY&Q3>b=MQ{yv7oAs&3-0XT{f-}crv^sKCM!KrZzgR|C45tHWt=N-4c(KsspV&%}r z|90fq`a|#e<{w;A2?PR35MX~oVh zVKoOXa!IH!<4-YjY;Mwp{^Z z)vWjDX1+K5S;@uw4-eL!_|<>;i+1^%RG}b+Dg=?vtVar*>)krnV>wnKAwV+nnuQ=J zA;Conf>=@*gxPEk9u|1vvD@*uJc}7kK>H4(;SjsKyI2B=01%{9P)c`&)FGA9QC4F4 zl|+z2LMjbLVYn@puGl_mYKK1RaYF14wZEqD6iHBtSgI$;a-;-FLka zqalIW!SWWys~ZR`kvkSHm&-E=Fok^NGi~lUUBCY;AGH8Z133L(ed~Yu@N72uNUtpZ zMPr&*+ui9q`~h`m0|93|rU6huQ7SkvlvK$w6JpDQrEWNg zZVpKSk^(XUG?91X{B?V}SKA*t!RgD}lzm!0a1w!qYgH5qcii$StQ|Up3+*3cFIW`1 zhg*)m624u9wW0Gf7l22?jX584007D7z~#8o`ryxhhymDN{lG8pZtrb<)O-8U@o0sH z{SmW(*qU#{2urdjAq$Ou(dYO5z`Orm=$miQJ^ATM4B!9N-}DWiUGJ@hwZSU&`+X1z zp3J5=vw50)<+-&j9U89U9dG*TOYE`pK6VRTdLp^eOjmx@QAi_WFs2qIlu}SqK`8|y zc|hMH1?K0^p9c$tQc76mVc}q`>x4NY9Vbqlz(4qgcVZB0P9cSY(h8C!q!2*rxRfhY znBp}V=h4g?_C5q_jGayD^kg!L)zxADb(fhj3`GtS~e)^WTzw63&*ey-rMHJ?45Lf2Ab{vn`@{0sP zY7OZev@T(-2MHIs>HxA*L3$Ru7dCPD$PoasCtmanBex|#^V@E{9fvn=O8u(QL9ocg zrR-ArI4sJC0Ph|9;CMQ%jkRXhw$1*NkDuK6)I*=8d+vVWH9ON+mnm#pa?U{)@kAf+ z%_yD1bRu)$!OS3`pmYHuf!S=UJvMjIlUZ2zTt>5&W zIJ>`jRtkx4{#);S#s~K;Z-3V_{+>e0eMS|F4njL%LzmA>?(3}KoJUc{0$6L2Do}u& z;bkyut*L2hoWF1$To6Pc1kZ*+irJ?mEIc!m^G?W)Qp;UH8kUNvVulqK zpIK}7o<8-|{GkUQ*q_z)<+0soxvs8RW7~EY$SGMOM1p@vK#+=ln8@kzs8&f3AW|7+ zh=uqZGMJYSf{d_S9<4w*EgE7vhA9vcYyHKip`XqA>Q`OrI()uMfWVa6%q7e@2Zw?V z;=tLZRZ5{Kdf-%?PGA_L8@5_0HjtNhG^iTLp+9y89dZQ3UM)nEQa zy}fg8zI9=z-?pX{f)qhW4lDu(@*#u}ybnGE?|pDC1ZQn<-g{$>X_~s-pG@kfpL)9f z%x6A5z2nY1&UklyhJ9Xr304Zl+oomPx5VrL?_ye?u&ILzDKMDzykFH7V%5> z$YOsOC@E1@C4?l%vCKc_TdL{ z!!EzB&Xlu9YBr7ENpf*8RIkfS(<#}g<7 zw#GYem=Hj4A#e@g@zHSj^J{CXA9>FYe~*374}Z^f*_*2p=;2_*Z?q=`XD?P)pX06a4S^3N9PLn?);-;ZOfU9_x1NW9^VuM5uF z*^^H`_4I5u`~9jK{LXMV_|wmP`k_mvH2vhi{@3FF`Hc@8ZktAjz^s(=MN!zIDE!Lm zn!n?YJKfzceVM!ar7!gx8ynm!V{vU=&oQrS0Eo>@nAsYGzxBq~@l&TByRLh4RRaA> z|L*7fo8SDE|KIlZ)(?3f-tK)^1H!;sGp^_JqhK5f?90vbr`6eWr>Sk5Xv&fw-OT5( z#=r-MUT=tA)sIhuBn}-qguCu~310t(H{g}8dQ}%#M4}l4hynt0UC*!Ujo;WSvx=gg zUZzn4DL6|dSRs|M2_aDyWvu?zMTAEMA-Fz_FdVDDHG~wjw>Q14`Tv3M|Na1A^BOMv z`lSS~u&$b%^oM`=;rhpZ>|gx$xpU`!dwXk}PMBC;=u>*!`ZW^F`e$<1uuFrUVPWfl0R+n`q#e}Z+^>LQz<>rG4ztE z7KpRL%;y;E_Iz5I7Y|y-CX&`pZCf(6ATT2WNd#SJa8NO@X9fhJP$dP&l1NCUG)W=I zi{~ygd#&qP(MNyeN8Wt^JALba@^@;fR2Yp{hHL8^{eCqNQc38dL|Il4QlTgmq*M?j z5u02`c#oKYAR%?xY4Z#}rQ%Lzz=Mdz=U+4bCAo4ou9N5k@kQ#)&77n?M6W~$Xr;UT z1p`6}%%vb9rJ~sNMJ&co+?eax_O?5)in4@|QkK15Un*6Am^g&kno3H@#FEQ{W|$*% zDx)9OWv_CDL^h9$sZ8*K1XwWi7pMd2jj%eZXp-#9)lcOA3@M{1gn&%_=`tBeDUnhE zq)3pEQc9(j@ZtJEXwSvECV`GPaq37t9sUQ78LlVomHB2N3b< z+Um7j>X+0?g}xy$dr4P%hMCzHx)CNKjejOMJbU_39X0lwLzg!ZVNd`0?Y{a;aZZD|-=GhQS!fb!!YxgHJcGsb$AOW0pz+5-laeOreX! z8A1qBO5@^M(C70KLXh`iK}t*nr7YeZQHhay)-4*@ESmo0!I^2kItf{nMOl`m<{LNw zzb1k9!2{hM<*q=9QMaf=!GdkDq#c$D1(+h^FbZ>6m{bHN5<)3`<2rT!5mqcs1M-d$ z4z_5^PDiQ}Iso%68_JX>&xCX#gp^uolGm@q?+d=JNuUujf}h#s$$g-UswS?8BGZ9y zd0ERuBsvfYQT8g7Wpz!L`lYnI_nq2UH1;`oVHfLR=SZBE7x@8ff)=IAx$Xcl$A=IcP~;J&*U^L zewTR&KIL};zS7WX({JJK!r?dhS z38{5SLP&<}PCo%1S(h2nJ-KA%j92QE&x_KccpwCM@BHz<>JAzF&C7tFB5zoFC4(H9R094Ii0B>0pkxIN$3ul(|_Q5*mi7V^A2qPAN( z4p}IQj6>(~8Mzy3d@RgLM!xbsjg*R4gV{pGA!6!O$` zWJ$m~Vefc$H!{EAdM=xW8O3tTtT`e@ba`c~n3F3PFD4*16V2Q_4n!oS5aN3EYJM)( zHC_2)!+abJc#)RKGWm=FA)_0mu3?uVXSkn7l>@`hgC`>Y`7BGqx=bvc_Dd^k{n zeWjIvXhDLWIoA9lU)P9*(ITIkar-W7wJ>!FM~ZK`XkRlCJ-*&A1&L`EJmT=5JTl>L z7LdsOk`i^4QZr3TtxI_+xfPZI?Zvy}>vFfMG`~lXproL0Tn&AV^ie>>c_cES%Gpg` zin(*(x11%+KhK4nu^CD>k0EuZOe&uV(JlF#g|51umMq^vh;+GKZ>qAt3JooYgcMRr zA%(nwU5;xKXlEVEXr&}0fP)8t5V|(8z@_d6FTPUmGwx;$CUQ;S1>BT^!W-BMUe_fA zEwZY4mnrdQvhqkHKslAUg%DdoURpZeSxq7mB1&_7pkJ^Xy3XsGj-u8&Gm{p2mzLY? zc8!maH*Xoh%RlY>=D-MPULbeANSwKOBL^zibG_?b-`vG}%-JMnItCroAG{P4CRzdx zJgub=KBfpMj(NUu_xc*?qXGcu(XElpkv3O!5L`t64;Z|xSdhoO#itWGv?DC}-;w!y zLq{steZ|0}Lyu@#Ko{RJkQ78340h4)f#`Qr5V37^{OpTV&W*LMEhy}!Tc^RGKs1NE zppwS5#OTQG9kMqO+XH9rMjBZ8vcgMu~?~~ICkP8WJyfFTz+MM4}cFYLr$0Z zEKX-4`Z6JPFTGLMHAzWb*O8ZW$!{)Yel5Pg^wr+y(Y-M-lV)-+J|qN|&%sEFFV;ng zR795&X3)V+oJUQ>@z0mMZkZb$E8Rpwn4Z%H^VhnrS?z^HwToJr5@&;R(<5;3U|vd! zWOxo?`74iobE7FI~x7V?hbUTeJu35b6-%yodGKjac~FxpQnrO|sw8 zQ4em=bo0~o$X!9%Y+Ewg%Dz;1%YyY_eL(Qzuv2(6wm@oCL|o1 zv-XX`>0Fw~)oJv_;*NzE!^yxQc<+7i@`jb@YeYE;5|k$UT(r2O*i$K)t}U_REJqU+d`IY_ zoT-Yh$?#jqKvP!SY0E-#GZ<-Xfsd8>!F%EmM0UWHdj6pt|Nes?BIg{w_G@CZ^k4YJ zU!v2ePvfut>aR&CA*B=~a}N}XQ({A4)lNT0%o2DL+$YZ2qt26JrHfQk8qiGs=?_rFAbDna$Tkk#n z`6oV!zyUw<-hT!kJnnzs0ovN$mVr){jN}o52FPYtjL9BY*@$ zDTS)nLsQpIh&am3+8dKQb8VfIVo<_@5Ec!nqED8^FEFE5^--1;tT(8t!O|yCVuYpc z{?4ZdKtSYj2mr(!zMx!=U;1~yLR;J0w7UkK5YQjVn%10h8z zgdC4XqoGn-pFO*I@9C${R0Kg%3KY6TQ5GnBJ?u^P(KH5HTTCYV*xA`eS@mJ9g;J_( z&nAR`Qabe@6agR$g4bG$x^BK;`+YsvHCC)hNTmdkkewGDQsxgKwlrYij@w^|jkTlj z?67g@P-JFsG>XJPa3Kk@hxf@viL-?BXsiRN#-IJSKS!sYIwj7WIU}~Vw#ClQwkpa} z3STaT7BcM+tdL%?iLn4y)FBO>Yx4kd#nGc%Eh z-RUBllSo~OYDmOF2q~o0Qp!>gNs!P=YXJx@ilVHWT1=TqA(SjB_4T^MIUs^A3TzzSKyG%jyci7UeH;nwLN|>xcu(GYNkp=3 znxPN^Kl6Y5@A5tG{wHtyr|XzuzUueo>e_0hwCW2f2Z4D2&=^ERA~67w zmQraVQUFV-AQNFhC1ET9XemV{q#OtmB@r=;*cUZvR7R{=VlcZjBuNpGqyt%wjx#|q zI!??^Atc47U4c7be!)Dv93YfwGDIYSsOi&8Z<11?C<8BC@eEdT)_3lP6&{7Bx^^3ANtPA}Wk9_8l;!i&Iu`|Jk3-A1g|LuO@up8L# zYgOzVKYo02?C8;1uUGQ7f7`cx(Fyytxg1iUkWx#n%C6lEG4gnXj)&XjW)G5QVw@Pz z8jHXT=RB-65x(;tZPRLJZGSeKZ73!qQClfQ9&BAOp3oEOu zVr_Lz7DZ7=sY<1EPfA%zDP;B?yX&GQpeqJTL_`89wv8$MSQQy7M9_TzL4ZY~BX>8K zeA)Cv$|-(|`vPW&=tilW=&eu+5AqKyyIOjC1oRpE}h(@x-Z#Gj{Xs zfA{O2ZcV%8oZAMl)2phjni67tH-TiH))SY$H z+)1F@P21cVfC2}pQXhIe$lyMjOa`QnI4%VaBu(sAOJ~3K~#6}^;cTmUM=>^IoGAK zm5LK#UBa4)V$W@G)X*|bag8{CK%kJ4Fbs5!9H~ph#;hTMNEA}Zjo?Ecj5Z!sMhGmV zl*EBO2ka^3Hm!F%6vD(gHDd?l0y6>+i&wP{DcU@Z> z11Zuhlr-FD*^y3bQJ_hkkT3u1y~lhugLMwIY2niYv&Nxr>H>hRs@`mGGND(0*=zWR zfB1j;)KC1xkAKQqBigp5q9|$JG_Q8v{Un25%gm*jNva7K}Z?YOxRj(VxF7I zN8uEy0ck2@LkI!RSQsGo?G`e%I*vhouP7j74`o@P-|u5QUcqQIq^e(*0zwKQN+|@G znRfPe{jr;F@}@PD>10;#?e5GcvuWM7O>ou~#+crPt@E!aisEPA_=Y$9PFZOCsr&x= z^WMMDXI)!jCH|AqqQy|b^Z*topAMMRC2AAm#ojxuqz1Fx z+uIArPu$Gk_kG{b-|-#)sLO@-hhsxTwP)W7B1Hrdf)my@sAtjslT=W;7lWZ*552ZU zGoNE`dneAomIXwbg`Hoc3yrFZ54$Le2#*N?#-b}~p%erO*s-Hs%n`#A0j1&y5T)Y1=t83?3uvX|{mJNt zL=Py(EP!(s)*3ixqw8lZd*WEY{IMkVK(N^m;v%y*`SvL{avVZ>r)zb}6Bhgj52B*6_^O+uOx( zIK*r3eJyUi`DSRP&?L7h=1v#BdZxrW=Mby|6JdX{kKNr}v`qsaJdPedgq4+5G&*OE3i0cO*;JoR4Z|_3oVz_?Tt_XtA16D*4Nf{-};ufd}cnM*>C)B{sI5kKmT#6svd4ScGI8GE< z?(San$vI{|Bm9`PpHIPVysyylzCAY2oWc2Xn{eJG;qx%g!Wavm4tQiB#?R~b2e|v? z_oV5sX}>}7z#{qSDXAioKfEYXCUD4BBgHpTK&08GlmvLujWCUE%1TNQbh1Vyh^wkk zqv7z#$wyD#w6{0e{(&EQ_uLr6qu~%rDgNEx{V+)>@wT^p#TOI_xvW6{{Lj6gK6U^7 z<=*bzxM|y!5JH~_nnUP;AOS%SJ@{!-N);r7d#D$lMOj8a-tHlDj?= zSwnZ4+8H=+!5qZO%IYgt*H(|e?Ci{1 z)6AT+Gp*EIkhqys)+0Bv0WBDPNN$4=B2MU%5+tHlCINV7!5puzP6j9YtXdVAPNw)j zf9==M)KeTfatv#S4}lUa7DvTI;mQ1psOkc`$yESOpE-jwXU^uRFMd%x2)ZaSURgm1 z9^2d7u+{)HfL0Ph#>bYM_e2H3os#g(9VIvZD!^Eakb}eE;n{bDrA+GgAwUSia5zR) zRq)=UuIF&h;T8A10{7l~Z-xn7Yg!|KH#JRrL_+uz+hb7t#P_uaP}Lf8{R zUii@;{g>M>zUxIZr8TGhV<7~}qCkI8;mg1LjRyq&V}J5-5<=n)umAk;?@I*wYrp1e zZ~f4RKKOPi)rypwq?CdLNhxzBCIv9OlsYJ_WNX{oY-^-|KoGGFtM?YpsfDkcHqOH* zo-QTf4lt&HwGKX}dsE4f>wH6i#QOT$@jGt6{m^Z<+>)E12qfCv%3T59Y&)u*4NfhmL;6C*xK60laD_J=Odz#4~nxE>^)l3#!!~xY;RqbD0ClS z!fd*awwWWa1E)cT`Tax$@1hke%N~v#iBreCcbLuQFs8-o$_nm&$xEP>LTikiPAApQ z&h}l?>FhQs9T!r!h0={uYPV^cnfGp9*UkPz4?Z}5_~D0V045=XJqB-EW45G}7ly;( z`G55j|I5xjuXuSIU8jJ}&GQg~@cSSB1HA1k-}0=5`VxT-0k;DDZ47@aASMi)5hTtA z#@Rxv^IDg-EP9pJg;rXYr%pXJ-rL=!vR9$dC8QK+ng$`lJWHRwkyW-1{2ziV}5Q<2Qfv1Ni7iKZ5CWf{l%J^r{jW z&UocLFT*?D@%N#%!q(On?*G*NxbIWoPu*6Qpal$Qoy?aMFR7B z4qX)R!9(i;>%AWOy%Klc`7#{ZSdB`>d05k8cYg;vds{en{w(%(cQI>c(FGI^^=yVc zB@seEJD)=;h2d}j;s6c~0xWuEZ@96xUJd&L?;VqKHngp=#zs4WTH8AB*@xh~ zb3SkgP1DSprr9^fG@F~}_D`OCboN`n{o8vCYzZlMgrEymuR33r<(YTC=Y3~i_qx~4 z4C@QWzJTxg&VO`?z+WoR(cxt+lu(5}tdzP*#Lgq+9dig~#({Rh zm`GVolq_yKeR>mu2&&M~N?|&k0n;?jEi=LLA4pk@++S^fe;-N}F_F@?>8nYa+ad%< z01tY-3Rr3v)_q)Lj8!3o;GK6;idHFU>v@g6{r%pq^<`}6Epg(+tvGSwX1w*SZ$+NH?AKt*mp$!yTL+b)cX@Gr-0is(XiAZa$h)4xTaKWN1%U%eB z90)T2vfg&Q`8U7iEBCjzx9w;&n*Yhi{*W&#(8d@C&^Xb!P z@W>;NkTE8rEFx!pV$dgqh}i3(!gw^si4!OAqPt#%vMAx4!-ef_oZCE$kAC#SICt(` z%mY$%KO(JSQ5z9Bz!;19Yz83(OfyID4!yF6^JmWkev#U)n-(XI-;9-&6*%j=1jukW z#C$f#XHPx~aJnUJptt=!Eo)$3yJw9>%<$qG{(CkH;vB5=tl(WdWsR3TmT8 zp)^Gcf}u#pcJ~CZAP5%3A)p`?c|JV2@*x~8t{nUqrb;KO`2=lOhU0PF_bFrDG;aKX?9KBQ7Gtwo_#4ASZvb=|-kgVwaz-;d#*`X#+PN!ltp=7>+&y; zA3xT~@g)L%$DMbaz3;yJey*{y3n|oBmu2r*RaHV4TIiyns#l>bd+1d?^sD$i zFL*C;vjt&L4KU~rIy3=@Jloc^Fe!-w#6T=C!v%&l7RDF|#4bUrE2~gS!+8g-D>FrUpM{MNSE-QDhDhYVj5k{>Q(>RPJ!{d6*eE;OW+05fK@ zIjnWDiibTsJCM$>1`x@JNsu@)N%DV37?I!uz)T2%(#VfEwptW1(A7nOvM5m&k$T!I zd#L&`0i}x)h1RI5J^%@012h<=l2B!VUdp}-DIrKj1t$`2oeg5*b|vK$?I12CtAgND))h1TemJq(8<3`Zjj1_KO-BMgS4n3YOb zo9owrhQd=3vC4wq@2T1YY)DtQsXe_0n zbrEBMvR6TIiK6JiTMH=_Adr$EE*{!8CXlT6kS-pk*0vC}OneMR)7EIr93gOWk^1Nl zS5Xu(OReykTK1b6u-nR9e1Mhhs&>C}Pfd+tI|MmZyfA9x?D4aNPeCvrPPW|y@ zRzIM1ac5DK!{B|dp3i79onktjVz=In+E5CRj8K;lgfii%>#OTnSzW>E>I%l=m3WHN zpO=z4Eh7^3QOS6YnM?EsQIPic_wna{{s{;o6k20A9$`ElW4toPcr?Pwcol{2q2I4y zjD>X;>-9PgO=oCL3zw1=wlQei8f|0HwgydG!#WFV9c*i0TugrWq!o>^&_xeSKBlA- zYMFIStWNh393MCdg~_bW$!iD*6lEX7;ShsCA636Xuc{!0f=(&+ve$zyHFQy+sw#{I zee?$-=p?97i)tvLqM#%UpHp%@TOSgDRvJ=7mkEhRDkUJ5i2RL6Jc`g{vH~)(??lr0 z+3G+LkS5Tk)Ir4ALn1aM_aSf`s^byT(1nP#$Crip86jfx3H|Rfs31<3ILZ#v`&n>G zNLe)X9FzSC=JN^W(|z9FI%l`HFF4b-J~;1#_a~)5-RYsr33OiXeeXY`M<0ER9)I%8 zPb*b?W3Sg6N}*MJs2Rr%sOuWj$pq8s6#J7s%;$4>=h959d7>&m`eB>xrR>oLe zTgA%C8p@)Gers|8@_T&8S6dzsnK=%53~)5FSp$IyBy*mDJM$ackww_5ty>slqA%}F z^oy4^LsIk+NeT$1qL4@lr8Kg;rF0CG2EzfyqXCA)0jmBGd%@4i85m@?jtC;e|>k>-I z_>dN23?iAW9;|cNn@lm;n_ynoXzCiRZQ(lM5vT?O9NIXHTW-A-_q^(bxZ{r7arE#Z zjD`c0{R&D-FaT#A+SXuNH<-<)nAZ)Q^&Kgxqr@U=I3<&tAfoB^0ccy-HLdJuvqTU= zL5Rh+FRJ51H#d+|zh_1y;>fq(M@ z|5vx4&nVDDkv0fI-Bo5>rf0(f8_pUPsHy?_{XRAh9fAPCdz&__LDQOqcr>uKg=uP> zdTI(E9!Ky2LP%6qAEVV3jD{l&hC>Voee|meRj-073lxP$S@a-u)`anp38}%N0jB^j z@|>f_mr6sZIN#d3fYvw|7vKUz`Lwyru*Rb`Hf1p5VNeRo;4p#!1Y}nL%S;dqP+;Ui zuYtLX5pq()C%-*InbxJC4^c3^^O0<1ebVNf0^Q{QyJ(8r*1%emER8vUAjN{)s4bm$ zn9ZiJCURz4lU~;}9n~NUS+sxx95QH;T$@E(iB9;sEO0usGFQta zbmv}X#7PPPrdYi$b1|<_2u$(%qEf0Ecu*34@vaxX;CEl~ikI_G|J48dxf3hPE5dFq zz;Ll{km;Ek`5mB$hD9(Q|CK0;3T4^P#|41{tZ8AApKDD-D6%`Sy}gCa&9iXMf&f%y ziNSD)UR6cm?hi5O_tCEg==FL42^izxT+BWSh%`e42?v_mPSjeR*pPoOKCXw+o(1`dO;>PClc9vFUflp|u94 zZDC9uS%}uarN0vbcWzSD#sJfTvYG|%wp(w*$&;T2k?7cUxkQJgNJ1DbjF2%16v(I# zbplEm0TLAXE+nF0cE3-r6;W^n#o{#*X*f}b9LdfN5=ualS_pCa%C4em4fdz|sOK{{ zXG3f3zW2ebt*>HZWBsx+_&Ef+i6#nMGP4LNAh@c<7k`_!o)3z-)+Mwq6P%T%S9=fJ znJ)`x1KOrR(?q{(XJ@yo+R!TE#6{VQJ~W{-w;BAWPZZQ=@GowKdW=qXxCk!MkW$b3uZ)7QrQtB zy!3J*Q4|uOBL4(PEU~~I0WtMk&*qrVW~k>gOegzjY?EN1gb`<4Jl8ZPzc*h_6n@b? zP%wAfxOjhtmf}5eSIrU+H>cZ6^ZpW?$K3>uEWa_hjM%SCFqx7%!kzcA)l2WP5`O(XZ7k8L$kQV^^vsjBW!N~+F2q-WFv<7;`0PE`;kccMBvcY6F z!F)2qWHP~QK7(y*_>0=f`b4eF*{hH~KjWkTI&&HYtVqJ9p|$R+LZqT7ph`o@BJmz1 z3SFe%$1g~N%y@13H)FMttDNAkX#JoW=L|W?O_(=lo(DgUo@xB7OZUytTT~en0$TXT#5?}T&jo}4M%vv3tj}F z$|&Y^P>e5KNrk8D6VEUh^oj!g!4H0rE?l?(r4`L4(|HJdelDj2t&36@MNes^1;yNE zHZ;qXDztpa4t|4Wld`PIYyqDKenMhASi^X@g2RUcCX;yhYaN`mDWQ=be6B`Ggm@qdS+)kT(|`ac zQ;H;H3OjzXGOD+>-a~!L0a^JOV(mNN6mpJE4DF+nN_2n_t z9J7wz104C@v6x`tRsoE8Jp(uvF~>cLF@Q7Cl`__(I3pxVsY6*RIA@`xLI^Px-Ghrbg%TGFb0-X^73&x@&?d8@L1$sK2VqVXwY33-4 z;zQfpJ5%TEZBoh?D6MZV%igeG_4`t(*nkvEr}v_>wmA9uA~W`R>6y%_+vU%@@@v!2 z54a2H%<1B`1rm6aPI)Bhm%W(881#pcufcJ!CAf|4d^s}O;q)=st;@1D(+SLHHL9w@6Hh)0l5%NU zz~y9=4*mzT00%UUp)0)M$^!lMU;p*|Z~o0sv-cif^`E}=6Q6nHk^4S%{{!oj$@F!B z{k=t@k4u5$QsO2c9P;cdXT9)&A%()SKZP7XP@eOVaNElWHs!#9vVWGVK9=|pSomj4 z$2XB|q8abBXZG82@Y7ujB~!OBv-z-b3S%-ta?t1QlrIPb?+xaY8LTm}Tq=0D0}b0F zJ{*JCTq_uB{bJpp1ql%=Qh*phf??V=!&Lwxja4a)#xy9(9>APj4u&o?jIkggQI!Q; zO1BdcTvhb2qm$#eICb- z9>bZlXHgaf2tZlH7-w~LEj6yGpp}MFx5E zrCs?u)ZZU{6Owg8mLba|w4kv}w(NVTtWi>pC3_kgV~NHdDZ8mbmXBp7lYJXSSwC5` z3>v24V~Y$L!c6$i|M0!f{q_EEpZnbBzV12abq;jbGAdopePq=nW(paN+^;HPcvO>i zJ=^ObuVgw{THRP@MRR4@Rd zu#99G=PU#y=9cu$y7BPQi9y6J=aRiFau;ssFT_rl zD1Uh-9}9}AY2qg}sLOSaTjMN(h#R?=^vCf8#$>^F#*{(jSNi(0ItIF>654^o&vjFck}Yj5o#SOy+C~OAZu>rTl?uvd z{|-Erd;HE&B^*ZL*hF6m^dAqaYzmCNzgg`&83m)0#Fr8Tx$gU|jW(GF1r-Kjx>moC zyzfjQG{qb?-*;6x+|cS2W7%DD>-v-FV#D7-=q&r8oH0KH-Pb`hA=myCS5mKdYxMk= z7n;sJm$;IsvK+KKuC9maIXO8@5()M__;|Bpmp(Nr_F+3b?eH+#plD=;!c@2a+A->O zy}I$V@$C8I<3BV{i@H*t6I(G5+E6=2vki|Y!{U6I{L@<;`)sc{!{;@H+E0Gz*GoK! z`i$n6zFqs92NmZ=xy^-a_G-luFRE{fgbW|J&c6Qzt22VZ{EVXPt*XHGt^1dHB^4j) zvDL`C_K9}|z_ON}Tv)9AW$o(KcO^i^TtcXg(6i+CayKGwsJurj?Xg{8BGbu5e&_=0@U_S`VWo z>3?;r!zAe`@WjKV1EhKxM~8H5Z$>$oQB0Tp>{ty>l(U^Ou3Dy#7&5rxxxGwAxyvS( z%y2kdAi0tKHpZ8$Yy@Q!=DvPK0Q|5)-0UHrcVRUF{e!rd5f4%l5`V~Q7aT}Um1(0# z8YVlXN#nH!z3YFdiVpQP2hDvQ(T_|G?l??R9a?b_*kPIBcTai5uo~pS-`@?s37W8$)CBefSIi-Qv?n&R5{;4 zdo_ww-xGX9;iDk5=8$pTfLYV#a)%WG$?iXrDRLUe_YT0 z(2IT_?j8RQrXMu&47Kksq;_ji)qn$*HNHPv)B>%UNO((R7Gka${pRSj1u7uF@Cu8J+2y{Mz(@|f%XaiFy_o}Yz5q9qrAs^ykxzic;HIvlD}B5@Vj_I@W(sQJt^}*`pLddy`L!tzZNs#|@E_p@p*r-&vs1 zOe_9GCc@rM@MjBtjjEwuFAb{3&xp&nR1r)U?$bUdR|0XCH`NE^IF>|A5yh6hzA%rjj(oL4A4(M6R-zBOh`r=gJr`Y!Ck6loyZ#gYyX-6N%DCB7(m$h43%Ka)N`=)HWjXFL_*%vdLwe!YT^T(OIOpyna^37 zcuYG+Q_tw$c(I#}ZV25q?&IN!ySlCES?h{gtb!M6-iW2=o0n%VXbF6?iN0v5UoFxa zj8rild}4>BMK$Q?9*3sYT6?Fr6@~>8|KMu4*2j<6*r}5$tB$Wq=W>cG@nE|5a5)C9U-T8)Ext{O+>9=L@K+(q|M?Mw z6>yQb6RmpTAK;yU5E#u1Tt^x}P^Q1Z|2kw&vE4nf$kF*3_KOM0&tQr23!GM1I2=~* zACbgQedU3*l=mo9?Ltpn>ip-plU)&wq!6hDDqrX6*a{Qw$pDzo_+@cC7_?X#S4v-c zGLuQ55rAw{q33KRG#JkB!y6_R;YSW5&b+?^TuWi=G}jc2<%6iP_s5oro1_<8gFFq@ z`c1D?Ex&)`=rmcNUu`rt=L>m!diz4!_&Nw;*1aA<3v;i3amIvu)?ivu7F&4r#qGW2{MTB%r8EypQdKMlCkxK&U zx1#7(2Ai83MM>f?Sr~^oOOt61oa+o5n`oO!APUk5kbZgP-qpp>DuT^*D2*olpW_h@ z=l6qzP_RF-nj9Gk6vvVCRtuHyoWM^cnX3~A;ry8{ZLqfE;U57ER?85ER@V@Hi~t hXk Date: Wed, 2 Nov 2022 13:34:38 +0100 Subject: [PATCH 325/327] CutGizmo: Bug Fixing : * Crash - when click to Whipe-tower * Crash - when the bed is empty and the top bar is pressed +. Note: There was a bug in detection if we can increase/recrease instances * After cutting the object in SLA does not work clipping of view Improvements : * Connectors mode of CutGizmo : Add "Cancel" button to Discard all conectors and switch to the CutPlane mode --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 37 ++++++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 +++-- src/slic3r/GUI/Plater.cpp | 9 +++++-- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 14b31d7c50..818872bb41 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -864,7 +864,10 @@ void GLGizmoCut3D::on_set_state() m_parent.request_extra_frame(); } else { - m_c->object_clipper()->release(); + if (auto oc = m_c->object_clipper()) { + oc->set_behavior(true, true, 0.); + oc->release(); + } m_selected.clear(); } force_update_clipper_on_render = m_state == On; @@ -1000,7 +1003,7 @@ 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) + if (object_idx < 0 || selection.is_wipe_tower()) return false; bool is_dowel_object = false; @@ -1391,7 +1394,7 @@ void GLGizmoCut3D::on_render() void GLGizmoCut3D::render_debug_input_window() { m_imgui->begin(wxString("DEBUG")); - +/* static bool hide_clipped = false; static bool fill_cut = false; static float contour_width = 0.4f; @@ -1405,7 +1408,7 @@ void GLGizmoCut3D::render_debug_input_window() oc->set_behavior(hide_clipped || m_connectors_editing, fill_cut || m_connectors_editing, double(contour_width)); ImGui::Separator(); - +*/ if (m_imgui->checkbox(_L("Render cut plane as circle"), m_cut_plane_as_circle)) m_plane.reset(); @@ -1527,10 +1530,18 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) ImGui::Separator(); if (m_imgui->button(_L("Confirm connectors"))) { - m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); unselect_all_connectors(); set_connectors_editing(false); } + + ImGui::SameLine(2.75f * m_label_width); + + if (m_imgui->button(_L("Cancel"))) { + m_clp_normal = m_c->object_clipper()->get_clipping_plane()->get_normal(); + reset_connectors(); + set_connectors_editing(false); + } } void GLGizmoCut3D::render_build_size() @@ -1573,6 +1584,8 @@ void GLGizmoCut3D::set_connectors_editing(bool connectors_editing) m_connectors_editing = connectors_editing; update_raycasters_for_picking(); + m_c->object_clipper()->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + m_parent.request_extra_frame(); } @@ -1603,6 +1616,13 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) reset_cut_plane(); m_imgui->disabled_end(); + ImGui::SameLine(2.25f * m_label_width); + ImGui::PushItemWidth(0.75f * m_label_width); + m_is_contour_changed = m_imgui->slider_float("contour width", &m_contour_width, 0.f, 3.f); + + if (auto oc = m_c->object_clipper(); oc && m_is_contour_changed) + oc->set_behavior(m_connectors_editing, m_connectors_editing, double(m_contour_width)); + if (wxGetApp().plater()->printer_technology() == ptFFF) { m_imgui->disabled_begin(!m_keep_upper || !m_keep_lower); if (m_imgui->button(_L("Add/Edit connectors"))) @@ -1641,7 +1661,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) ImGui::Separator(); - m_imgui->disabled_begin(!can_perform_cut()); + m_imgui->disabled_begin(!m_is_contour_changed && !can_perform_cut()); if(m_imgui->button(_L("Perform cut"))) perform_cut(m_parent.get_selection()); m_imgui->disabled_end(); @@ -1729,6 +1749,8 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) void GLGizmoCut3D::render_input_window_warning() const { + if (m_is_contour_changed) + return; 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)) @@ -1829,7 +1851,8 @@ void GLGizmoCut3D::render_connectors() { ::glEnable(GL_DEPTH_TEST); - if (cut_line_processing() || m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) + if (m_is_contour_changed || cut_line_processing() || + m_connector_mode == CutConnectorMode::Auto || !m_c->selection_info()) return; const ModelObject* mo = m_c->selection_info()->model_object(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 35b6a92cec..8a3ee6d074 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -96,7 +96,7 @@ class GLGizmoCut3D : public GLGizmoBase bool m_hide_cut_plane{ false }; bool m_connectors_editing{ false }; - bool m_cut_plane_as_circle{ false }; + bool m_cut_plane_as_circle{ true }; float m_connector_depth_ratio{ 3.f }; float m_connector_size{ 2.5f }; @@ -109,6 +109,9 @@ class GLGizmoCut3D : public GLGizmoBase bool m_imperial_units{ false }; bool force_update_clipper_on_render{false}; + float m_contour_width{ 0.4f }; + bool m_is_contour_changed{ false }; + mutable std::vector m_selected; // which pins are currently selected int m_selected_count{ 0 }; @@ -170,7 +173,6 @@ public: 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; } void invalidate_cut_plane(); BoundingBoxf3 bounding_box() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 63b0a4e217..dd47325b5f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4934,7 +4934,9 @@ bool Plater::priv::can_increase_instances() const || q->canvas3D()->get_gizmos_manager().is_in_editing_mode()) return false; - return !sidebar->obj_list()->has_selected_cut_object(); + const int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && + !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_decrease_instances() const @@ -4943,7 +4945,10 @@ bool Plater::priv::can_decrease_instances() const || q->canvas3D()->get_gizmos_manager().is_in_editing_mode()) return false; - return !sidebar->obj_list()->has_selected_cut_object(); + const int obj_idx = get_selected_object_idx(); + return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && + (model.objects[obj_idx]->instances.size() > 1) && + !sidebar->obj_list()->has_selected_cut_object(); } bool Plater::priv::can_split_to_objects() const From 0468250298e76c7be5b4ca9e96175204f7847ebc Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Nov 2022 16:26:09 +0100 Subject: [PATCH 326/327] ObjectDataViewModel: Fixed a check of m_bitmap_cache existence. There was a crash after app's recreation, when we try to get some bitmap from m_bitmap_cache, but it is null for this moment --- src/slic3r/GUI/ObjectDataViewModel.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 50577771bc..066fc45223 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -19,6 +19,14 @@ wxDEFINE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent); BitmapCache* m_bitmap_cache = nullptr; +wxBitmapBundle* find_bndl(const std::string& bmp_name) +{ + if (!m_bitmap_cache) + m_bitmap_cache = new BitmapCache; + + return m_bitmap_cache->find_bndl(bmp_name); +} + // ***************************************************************************** // ---------------------------------------------------------------------------- // ObjectDataViewModelNode @@ -186,7 +194,7 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps() std::string scaled_bitmap_name = m_name.ToUTF8().data(); scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : ""); - wxBitmapBundle *bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); + wxBitmapBundle *bmp = find_bndl(scaled_bitmap_name); if (bmp == nullptr) { std::vector bmps; for (auto& category : m_opt_categories) @@ -321,8 +329,6 @@ static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType roo ObjectDataViewModel::ObjectDataViewModel() { - m_bitmap_cache = new Slic3r::GUI::BitmapCache; - m_volume_bmps = MenuFactory::get_volume_bitmaps(); m_warning_bmp = *get_bmp_bundle(WarningIcon); m_warning_manifold_bmp = *get_bmp_bundle(WarningManifoldIcon); @@ -359,7 +365,7 @@ void ObjectDataViewModel::UpdateBitmapForNode(ObjectDataViewModelNode* node) scaled_bitmap_name += std::to_string(vol_type); scaled_bitmap_name += (wxGetApp().dark_mode() ? "-dm" : "-lm"); - wxBitmapBundle* bmp = m_bitmap_cache->find_bndl(scaled_bitmap_name); + wxBitmapBundle* bmp = find_bndl(scaled_bitmap_name); if (!bmp) { std::vector bmps; if (node->has_warning_icon()) From a59f8aea6ef7dd5ff04eda5835617caf2b774c35 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 2 Nov 2022 16:27:49 +0100 Subject: [PATCH 327/327] Follow-up https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/ba22eb600e62ddaee0a83f2037c4bc0c3ccd3cdd - Fix for string formatting (by @bubnikv) --- src/slic3r/GUI/format.hpp | 69 ++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/format.hpp b/src/slic3r/GUI/format.hpp index 51beafff13..15a3f04c38 100644 --- a/src/slic3r/GUI/format.hpp +++ b/src/slic3r/GUI/format.hpp @@ -7,12 +7,35 @@ // though C++20 format uses a different template pattern for position independent parameters. // This wrapper also manages implicit conversion from wxString to UTF8 and format_wxstr() variants are provided to format into wxString. -#include - #include -namespace Slic3r { -namespace GUI { +namespace Slic3r::internal::format { + // Wrapper around wxScopedCharBuffer to indicate that the content is UTF8 formatted. + struct utf8_buffer { + // wxScopedCharBuffer is reference counted, therefore copying by value is cheap. + wxScopedCharBuffer data; + }; + inline std::ostream& operator<<(std::ostream& os, const utf8_buffer &v) { + os << v.data.data(); + return os; + } + // Accept wxString and convert it to UTF8 to be processed by Slic3r::format(). + inline const utf8_buffer cook(const wxString& arg) { + return utf8_buffer{ arg.ToUTF8() }; + } + // Vojtech seemingly does not understand perfect forwarding: + // Why Slic3r::internal::format::cook(T&& arg) is taken for non-const wxString reference? + inline const utf8_buffer cook(wxString& arg) { + return utf8_buffer{ arg.ToUTF8() }; + } + inline const utf8_buffer cook(wxString&& arg) { + return utf8_buffer{ arg.ToUTF8() }; + } +} + +#include + +namespace Slic3r::GUI { // Format input mixing UTF8 encoded strings (const char*, std::string) and wxStrings, return a wxString. template @@ -42,42 +65,6 @@ inline std::string format(const wxString& fmt, TArgs&&... args) { return Slic3r::format(fmt.ToUTF8().data(), std::forward(args)...); } -} // namespace GUI - -namespace internal { - namespace format { - // Wrapper around wxScopedCharBuffer to indicate that the content is UTF8 formatted. - struct utf8_buffer { - // wxScopedCharBuffer is reference counted, therefore copying by value is cheap. - wxScopedCharBuffer data; - }; - // Accept wxString and convert it to UTF8 to be processed by Slic3r::format(). - inline const utf8_buffer cook(const wxString &arg) { - return utf8_buffer { arg.ToUTF8() }; - } - // Vojtech seemingly does not understand perfect forwarding: - // Why Slic3r::internal::format::cook(T&& arg) is taken for non-const wxString reference? - inline const utf8_buffer cook(wxString &arg) { - return utf8_buffer { arg.ToUTF8() }; - } - inline const utf8_buffer cook(wxString &&arg) { - return utf8_buffer{ arg.ToUTF8() }; - } - } -} - -} // namespace Slic3r - -namespace boost { - namespace io { - namespace detail { - // Adaptor for boost::format to accept wxString converted to UTF8. - inline std::ostream& operator<<(std::ostream& os, const Slic3r::internal::format::utf8_buffer& str) { - os << str.data.data(); - return os; - } - } - } -} +} // namespace Slic3r::GUI #endif /* slic3r_GUI_format_hpp_ */