From 22ccb5657834e1b2fe548dc647a77044bc9c38f4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 09:07:03 +0100 Subject: [PATCH 01/56] Added class TransformationSVD to detect transformation matrix components using singular value decomposition --- src/libslic3r/Geometry.cpp | 44 ++++++++++++++++++++++++++++++++++++++ src/libslic3r/Geometry.hpp | 19 ++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index f7f6bb2f99..884122b800 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -838,6 +838,50 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation } #endif // !ENABLE_WORLD_COORDINATE +#if ENABLE_WORLD_COORDINATE +TransformationSVD::TransformationSVD(const Transform3d& trafo) +{ + const Matrix3d m = trafo.matrix().block<3, 3>(0, 0); + const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); + u = svd.matrixU(); + v = svd.matrixV(); + s = svd.singularValues().asDiagonal(); + + mirror = m.determinant() < 0.0; + scale = !s.isApprox(Matrix3d::Identity()); + anisotropic_scale = std::abs(s(0, 0) - s(1, 1)) > EPSILON || std::abs(s(1, 1) - s(2, 2)) > EPSILON; + rotation = v.isApprox(u.transpose()); + + rotation_90_degrees = true; + if (rotation) { + for (int i = 0; i < 3; ++i) { + const Vec3d row = u.row(i).cwiseAbs(); + if ((std::abs(row[0] - 1.0) > EPSILON && row[1] > EPSILON && row[2] > EPSILON) || + (row[0] > EPSILON && std::abs(row[1] - 1.0) > EPSILON && row[2] > EPSILON) || + (row[0] > EPSILON && row[1] > EPSILON && std::abs(row[2] - 1.0) > EPSILON)) { + rotation_90_degrees = false; + break; + } + } + } + else + rotation_90_degrees = true; + + skew = false; + if (anisotropic_scale) { + for (int i = 0; i < 3; ++i) { + const Vec3d row = v.row(i).cwiseAbs(); + if ((std::abs(row[0] - 1.0) > EPSILON && row[1] > EPSILON && row[2] > EPSILON) || + (row[0] > EPSILON && std::abs(row[1] - 1.0) > EPSILON && row[2] > EPSILON) || + (row[0] > EPSILON && row[1] > EPSILON && std::abs(row[2] - 1.0) > EPSILON)) { + skew = true; + break; + } + } + } +} +#endif // ENABLE_WORLD_COORDINATE + // For parsing a transformation matrix from 3MF / AMF. Transform3d transform3d_from_string(const std::string& transform_str) { diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index aa37d94dd2..7e1624afc6 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -538,6 +538,25 @@ private: #endif // ENABLE_WORLD_COORDINATE }; +#if ENABLE_WORLD_COORDINATE +struct TransformationSVD +{ + Matrix3d u = Matrix3d::Identity(); + Matrix3d s = Matrix3d::Identity(); + Matrix3d v = Matrix3d::Identity(); + + bool mirror{ false }; + bool scale{ false }; + bool anisotropic_scale{ false }; + bool rotation{ false }; + bool rotation_90_degrees{ false }; + bool skew{ false }; + + explicit TransformationSVD(const Transformation& trafo) : TransformationSVD(trafo.get_matrix()) {} + explicit TransformationSVD(const Transform3d& trafo); +}; +#endif // ENABLE_WORLD_COORDINATE + // For parsing a transformation matrix from 3MF / AMF. extern Transform3d transform3d_from_string(const std::string& transform_str); From e3a20bf6c498d9d0a5d74eec87030745617a2981 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 10:00:57 +0100 Subject: [PATCH 02/56] Added SVD decomposition to transformation debug imgui dialog --- src/slic3r/GUI/Selection.cpp | 57 ++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index dc016f2141..4085e9801d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2824,7 +2824,7 @@ void Selection::render_debug_window() const } static int current_method_idx = 0; - ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0"); + ImGui::Combo("Decomposition method", ¤t_method_idx, "computeRotationScaling\0computeScalingRotation\0SVD\0"); const GLVolume& v = *get_volume(current_vol_idx); @@ -2844,23 +2844,56 @@ void Selection::render_debug_window() const ImGui::EndGroup(); }; - auto add_matrices_set = [add_matrix](const std::string& name, const Transform3d& m, size_t method) { + auto add_matrices_set = [&imgui, add_matrix](const std::string& name, const Transform3d& m, size_t method) { static unsigned int counter = 0; ++counter; if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) { add_matrix("Full", m, 4); - Matrix3d rotation; - Matrix3d scale; - if (method == 0) - m.computeRotationScaling(&rotation, &scale); - else - m.computeScalingRotation(&scale, &rotation); + if (method == 0 || method == 1) { + Matrix3d rotation; + Matrix3d scale; + if (method == 0) + m.computeRotationScaling(&rotation, &scale); + else + m.computeScalingRotation(&scale, &rotation); - ImGui::SameLine(); - add_matrix("Rotation component", Transform3d(rotation), 3); - ImGui::SameLine(); - add_matrix("Scale component", Transform3d(scale), 3); + ImGui::SameLine(); + add_matrix("Rotation component", Transform3d(rotation), 3); + ImGui::SameLine(); + add_matrix("Scale component", Transform3d(scale), 3); + } + else { + const Geometry::TransformationSVD svd(m); + + ImGui::SameLine(); + add_matrix("U", Transform3d(svd.u), 3); + ImGui::SameLine(); + add_matrix("S", Transform3d(svd.s), 3); + ImGui::SameLine(); + add_matrix("V", Transform3d(svd.v), 3); + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + float spacing = 0.0f; + if (svd.rotation) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.rotation_90_degrees ? "Rotation 90 degs" : "Rotation"); + spacing = 10.0f; + } + if (svd.scale) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, svd.anisotropic_scale ? "Anisotropic scale" : "Isotropic scale"); + spacing = 10.0f; + } + if (svd.mirror) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Mirror"); + spacing = 10.0f; + } + if (svd.skew) { + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Skew"); + } + } } }; From f3fda618e9fdf0e45f1ae1130ab846e26a607ece Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 10:49:02 +0100 Subject: [PATCH 03/56] Method ObjectManipulation::update_reset_buttons_visibility() modified to detect reset buttons visibility using SVD decomposition of transformation matrix --- src/libslic3r/Geometry.cpp | 4 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 126 +++++++++++----------- 2 files changed, 69 insertions(+), 61 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 884122b800..08f433c9a7 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -841,6 +841,8 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation #if ENABLE_WORLD_COORDINATE TransformationSVD::TransformationSVD(const Transform3d& trafo) { + static const Matrix3d mirror_x = Geometry::scale_transform({ -1.0, 1.0, 1.0 }).linear(); + const Matrix3d m = trafo.matrix().block<3, 3>(0, 0); const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); u = svd.matrixU(); @@ -850,7 +852,7 @@ TransformationSVD::TransformationSVD(const Transform3d& trafo) mirror = m.determinant() < 0.0; scale = !s.isApprox(Matrix3d::Identity()); anisotropic_scale = std::abs(s(0, 0) - s(1, 1)) > EPSILON || std::abs(s(1, 1) - s(2, 2)) > EPSILON; - rotation = v.isApprox(u.transpose()); + rotation = mirror ? !u.isApprox(mirror_x) : !v.isApprox(u.transpose()); rotation_90_degrees = true; if (rotation) { diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index c295eb8fb7..903181010e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -927,93 +927,48 @@ void ObjectManipulation::update_if_dirty() +#if ENABLE_WORLD_COORDINATE void ObjectManipulation::update_reset_buttons_visibility() { GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); if (!canvas) return; - const Selection& selection = canvas->get_selection(); + bool show_drop_to_bed = false; bool show_rotation = false; bool show_scale = false; - bool show_drop_to_bed = false; -#if ENABLE_WORLD_COORDINATE + bool show_mirror = false; bool show_skew = false; - bool show_mirror_warning = false; + const Selection& selection = canvas->get_selection(); if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { const double min_z = selection.is_single_full_instance() ? selection.get_scaled_instance_bounding_box().min.z() : get_volume_min_z(*selection.get_first_volume()); show_drop_to_bed = std::abs(min_z) > EPSILON; const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo; -#else - if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { - const GLVolume* volume = selection.get_first_volume(); - Vec3d rotation; - Vec3d scale; - double min_z = 0.0; -#endif // ENABLE_WORLD_COORDINATE + Geometry::Transformation trafo = selection.is_single_full_instance() ? volume->get_instance_transformation() : volume->get_volume_transformation(); - if (selection.is_single_full_instance()) { -#if ENABLE_WORLD_COORDINATE - trafo = volume->get_instance_transformation(); - const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int id : idxs) { - const Geometry::Transformation world_trafo(selection.get_volume(id)->world_matrix()); - show_skew |= world_trafo.has_skew(); - show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0; - } -#else - rotation = volume->get_instance_rotation(); - scale = volume->get_instance_scaling_factor(); - min_z = selection.get_scaled_instance_bounding_box().min.z(); -#endif // ENABLE_WORLD_COORDINATE - } - else { -#if ENABLE_WORLD_COORDINATE - Geometry::Transformation trafo = volume->get_volume_transformation(); - const Geometry::Transformation world_trafo(volume->world_matrix()); - show_skew |= world_trafo.has_skew(); - show_mirror_warning |= world_trafo.get_matrix().matrix().determinant() < 0.0; -#else - rotation = volume->get_volume_rotation(); - scale = volume->get_volume_scaling_factor(); - min_z = get_volume_min_z(*volume); -#endif // ENABLE_WORLD_COORDINATE - } -#if ENABLE_WORLD_COORDINATE - const Transform3d rotation = trafo.get_rotation_matrix(); - const Transform3d scale = trafo.get_scaling_factor_matrix(); - show_rotation = show_mirror_warning ? !trafo.get_matrix().matrix().block<3, 3>(0, 0).isDiagonal() : !rotation.isApprox(Transform3d::Identity()); - show_scale = !scale.isApprox(Transform3d::Identity()); -#else - show_rotation = !rotation.isApprox(Vec3d::Zero()); - show_scale = !scale.isApprox(Vec3d::Ones()); - show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; -#endif // ENABLE_WORLD_COORDINATE + const Geometry::TransformationSVD trafo_svd(trafo); + show_rotation = trafo_svd.rotation; + show_scale = trafo_svd.scale; + show_mirror = trafo_svd.mirror; + show_skew = trafo_svd.skew; } -#if ENABLE_WORLD_COORDINATE - wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed, show_skew, show_mirror_warning] { -#else - wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] { -#endif // ENABLE_WORLD_COORDINATE + wxGetApp().CallAfter([this, show_drop_to_bed, show_rotation, show_scale, show_mirror, show_skew] { // There is a case (under OSX), when this function is called after the Manipulation panel is hidden // So, let check if Manipulation panel is still shown for this moment if (!this->IsShown()) return; + m_drop_to_bed_button->Show(show_drop_to_bed); m_reset_rotation_button->Show(show_rotation); m_reset_scale_button->Show(show_scale); - m_drop_to_bed_button->Show(show_drop_to_bed); -#if ENABLE_WORLD_COORDINATE + m_mirror_warning_bitmap->SetBitmap(show_mirror ? m_manifold_warning_bmp.bmp() : wxNullBitmap); + m_mirror_warning_bitmap->SetMinSize(show_mirror ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0)); + m_mirror_warning_bitmap->SetToolTip(show_mirror ? _L("Left handed") : ""); m_reset_skew_button->Show(show_skew); m_skew_label->Show(show_skew); - m_mirror_warning_bitmap->SetBitmap(show_mirror_warning ? m_manifold_warning_bmp.bmp() : wxNullBitmap); - m_mirror_warning_bitmap->SetMinSize(show_mirror_warning ? m_manifold_warning_bmp.GetSize() : wxSize(0, 0)); - m_mirror_warning_bitmap->SetToolTip(show_mirror_warning ? _L("Left handed") : ""); -#endif // ENABLE_WORLD_COORDINATE // Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time Sidebar& panel = wxGetApp().sidebar(); @@ -1024,6 +979,57 @@ void ObjectManipulation::update_reset_buttons_visibility() } }); } +#else +void ObjectManipulation::update_reset_buttons_visibility() +{ + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + if (!canvas) + return; + const Selection& selection = canvas->get_selection(); + + bool show_rotation = false; + bool show_scale = false; + bool show_drop_to_bed = false; + if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { + const GLVolume* volume = selection.get_first_volume(); + Vec3d rotation; + Vec3d scale; + double min_z = 0.0; + + if (selection.is_single_full_instance()) { + rotation = volume->get_instance_rotation(); + scale = volume->get_instance_scaling_factor(); + min_z = selection.get_scaled_instance_bounding_box().min.z(); + } + else { + rotation = volume->get_volume_rotation(); + scale = volume->get_volume_scaling_factor(); + min_z = get_volume_min_z(*volume); + } + show_rotation = !rotation.isApprox(Vec3d::Zero()); + show_scale = !scale.isApprox(Vec3d::Ones()); + show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD; + } + + wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] { + // There is a case (under OSX), when this function is called after the Manipulation panel is hidden + // So, let check if Manipulation panel is still shown for this moment + if (!this->IsShown()) + return; + m_reset_rotation_button->Show(show_rotation); + m_reset_scale_button->Show(show_scale); + m_drop_to_bed_button->Show(show_drop_to_bed); + + // Because of CallAfter we need to layout sidebar after Show/hide of reset buttons one more time + Sidebar& panel = wxGetApp().sidebar(); + if (!panel.IsFrozen()) { + panel.Freeze(); + panel.Layout(); + panel.Thaw(); + } + }); +} +#endif // ENABLE_WORLD_COORDINATE From 8446eff59c2b57244cf477e5fe5036734bbed075 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 10:58:48 +0100 Subject: [PATCH 04/56] Method Transformation::reset_skew() modified to use SVD decomposition of transformation matrix --- src/libslic3r/Geometry.cpp | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 08f433c9a7..ec37766473 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -722,26 +722,15 @@ void Transformation::reset() #if ENABLE_WORLD_COORDINATE void Transformation::reset_skew() { - Matrix3d rotation; - Matrix3d scale; - m_matrix.computeRotationScaling(&rotation, &scale); + auto new_scale_factor = [](const Matrix3d& s) { +// return s.determinant(); // scale determinant + return (s(0, 0) + s(1, 1) + s(2, 2)) / 3.0; // scale average +// return std::max(s(0, 0), std::max(s(1, 1), s(2, 2))); // max scale +// return std::min(s(0, 0), std::min(s(1, 1), s(2, 2))); // min scale + }; - const double average_scale = std::cbrt(scale(0, 0) * scale(1, 1) * scale(2, 2)); - - scale(0, 0) = is_left_handed() ? -average_scale : average_scale; - scale(1, 1) = average_scale; - scale(2, 2) = average_scale; - - scale(0, 1) = 0.0; - scale(0, 2) = 0.0; - scale(1, 0) = 0.0; - scale(1, 2) = 0.0; - scale(2, 0) = 0.0; - scale(2, 1) = 0.0; - - const Vec3d offset = get_offset(); - m_matrix = rotation * scale; - m_matrix.translation() = offset; + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()); } Transform3d Transformation::get_matrix_no_offset() const From cdd6ce90792a3bdb34338704a82eeff81fde5262 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 11:04:13 +0100 Subject: [PATCH 05/56] Simplified ObjectManipulation::update_mirror_buttons_visibility() --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 903181010e..d1f065221e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1035,18 +1035,19 @@ void ObjectManipulation::update_reset_buttons_visibility() void ObjectManipulation::update_mirror_buttons_visibility() { +#if ENABLE_WORLD_COORDINATE + const bool can_mirror = wxGetApp().plater()->can_mirror(); + for (ScalableButton* button : m_mirror_buttons) { + button->Enable(can_mirror); + } +#else GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); -#if ENABLE_WORLD_COORDINATE - if (is_local_coordinates()) { - if (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) { -#else std::array new_states = { mbHidden, mbHidden, mbHidden }; if (!m_world_coordinates) { if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { -#endif // ENABLE_WORLD_COORDINATE const GLVolume* volume = selection.get_first_volume(); Vec3d mirror; @@ -1055,19 +1056,10 @@ void ObjectManipulation::update_mirror_buttons_visibility() else mirror = volume->get_volume_mirror(); -#if !ENABLE_WORLD_COORDINATE for (unsigned char i=0; i<3; ++i) new_states[i] = (mirror[i] < 0. ? mbActive : mbShown); -#endif // !ENABLE_WORLD_COORDINATE } } - -#if ENABLE_WORLD_COORDINATE - const bool can_mirror = wxGetApp().plater()->can_mirror(); - for (ScalableButton* button : m_mirror_buttons) { - button->Enable(can_mirror); - } -#else else { // the mirroring buttons should be hidden in world coordinates, // unless we make it actually mirror in world coords. From f2b986af578e3e2aea5970757d28c731b19669a9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 11:42:21 +0100 Subject: [PATCH 06/56] Method Transformation::reset_scaling_factor() modified to use SVD decomposition of transformation matrix and object manipulator reset button set to use it --- src/libslic3r/Geometry.cpp | 6 ++++++ src/libslic3r/Geometry.hpp | 2 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 17 ++++++----------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index ec37766473..c815719b7a 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -720,6 +720,12 @@ void Transformation::reset() } #if ENABLE_WORLD_COORDINATE +void Transformation::reset_scaling_factor() +{ + const Geometry::TransformationSVD svd(*this); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()); +} + void Transformation::reset_skew() { auto new_scale_factor = [](const Matrix3d& s) { diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 7e1624afc6..531f7cac80 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -493,7 +493,7 @@ public: #if ENABLE_WORLD_COORDINATE void reset_offset() { set_offset(Vec3d::Zero()); } void reset_rotation() { set_rotation(Vec3d::Zero()); } - void reset_scaling_factor() { set_scaling_factor(Vec3d::Ones()); } + void reset_scaling_factor(); void reset_mirror() { set_mirror(Vec3d::Ones()); } void reset_skew(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index d1f065221e..a80b47d2dd 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -484,24 +484,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); if (selection.is_single_volume_or_modifier()) { - const bool is_left_handed = selection.get_first_volume()->get_volume_transformation().is_left_handed(); - const_cast(selection.get_first_volume())->set_volume_scaling_factor(Vec3d::Ones()); - if (is_left_handed) - const_cast(selection.get_first_volume())->set_volume_mirror({ -1.0 , 1.0, 1.0 }); + Geometry::Transformation trafo = selection.get_first_volume()->get_volume_transformation(); + trafo.reset_scaling_factor(); + const_cast(selection.get_first_volume())->set_volume_transformation(trafo); } else if (selection.is_single_full_instance()) { - const bool is_left_handed = selection.get_first_volume()->get_instance_transformation().is_left_handed(); - for (unsigned int idx : selection.get_volume_idxs()) { - const_cast(selection.get_volume(idx))->set_instance_scaling_factor(Vec3d::Ones()); - if (is_left_handed) - const_cast(selection.get_volume(idx))->set_instance_mirror({ -1.0 , 1.0, 1.0 }); - } + Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation(); + trafo.reset_scaling_factor(); + const_cast(selection.get_first_volume())->set_instance_transformation(trafo); } else return; canvas->do_scale(L("Reset scale")); - UpdateAndShow(true); #else Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Reset scale")); From d98fd64534570e406cabef8895fc98c34687d18f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 12:40:28 +0100 Subject: [PATCH 07/56] Method Transformation::reset_rotation() modified to use SVD decomposition of transformation matrix and object manipulator reset button set to use it --- src/libslic3r/Geometry.cpp | 7 +++++++ src/libslic3r/Geometry.hpp | 2 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 22 ++++++++++++++++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index c815719b7a..f3b8926d92 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -720,6 +720,13 @@ void Transformation::reset() } #if ENABLE_WORLD_COORDINATE +void Transformation::reset_rotation() +{ + const Geometry::TransformationSVD svd(*this); + const Transform3d mirror = is_left_handed() ? Geometry::scale_transform({ -1.0, 1.0, 1.0 }) : Transform3d::Identity(); + m_matrix = get_offset_matrix() * mirror * Transform3d(svd.v * svd.s * svd.v.transpose()); +} + void Transformation::reset_scaling_factor() { const Geometry::TransformationSVD svd(*this); diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 531f7cac80..f7b08b9b29 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -492,7 +492,7 @@ public: void reset(); #if ENABLE_WORLD_COORDINATE void reset_offset() { set_offset(Vec3d::Zero()); } - void reset_rotation() { set_rotation(Vec3d::Zero()); } + void reset_rotation(); void reset_scaling_factor(); void reset_mirror() { set_mirror(Vec3d::Ones()); } void reset_skew(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index a80b47d2dd..1d6212ce58 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -448,14 +448,21 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : Selection& selection = canvas->get_selection(); #if ENABLE_WORLD_COORDINATE - if (selection.is_single_volume_or_modifier()) + if (selection.is_single_volume_or_modifier()) { + GLVolume* vol = const_cast(selection.get_first_volume()); + Geometry::Transformation trafo = vol->get_volume_transformation(); + trafo.reset_rotation(); + vol->set_volume_transformation(trafo); + } #else if (selection.is_single_volume() || selection.is_single_modifier()) -#endif // ENABLE_WORLD_COORDINATE const_cast(selection.get_first_volume())->set_volume_rotation(Vec3d::Zero()); +#endif // ENABLE_WORLD_COORDINATE else if (selection.is_single_full_instance()) { + Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation(); + trafo.reset_rotation(); for (unsigned int idx : selection.get_volume_idxs()) { - const_cast(selection.get_volume(idx))->set_instance_rotation(Vec3d::Zero()); + const_cast(selection.get_volume(idx))->set_instance_transformation(trafo); } } else @@ -484,14 +491,17 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); Selection& selection = canvas->get_selection(); if (selection.is_single_volume_or_modifier()) { - Geometry::Transformation trafo = selection.get_first_volume()->get_volume_transformation(); + GLVolume* vol = const_cast(selection.get_first_volume()); + Geometry::Transformation trafo = vol->get_volume_transformation(); trafo.reset_scaling_factor(); - const_cast(selection.get_first_volume())->set_volume_transformation(trafo); + vol->set_volume_transformation(trafo); } else if (selection.is_single_full_instance()) { Geometry::Transformation trafo = selection.get_first_volume()->get_instance_transformation(); trafo.reset_scaling_factor(); - const_cast(selection.get_first_volume())->set_instance_transformation(trafo); + for (unsigned int idx : selection.get_volume_idxs()) { + const_cast(selection.get_volume(idx))->set_instance_transformation(trafo); + } } else return; From aa9e7a8ae738987aaab1e0545a9a826b1cf2de5d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 2 Feb 2023 14:56:33 +0100 Subject: [PATCH 08/56] Fixed assert when scaling multi objects selection using the sidebar panel --- src/slic3r/GUI/Selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 4085e9801d..347cbc4efa 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1484,11 +1484,11 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation original_box = get_full_unscaled_instance_local_bounding_box(); relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size()); - transformation_type.set_relative(); } } else { } + transformation_type.set_relative(); } if (m_mode == Instance) { From e3cc3be9ab6a047503d0956f76cabffe7f167ffd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 3 Feb 2023 09:41:43 +0100 Subject: [PATCH 09/56] Volumes always rotate as rigid bodies --- src/slic3r/GUI/Selection.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 347cbc4efa..c3b0b57251 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1074,12 +1074,11 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center); } else { - if (transformation_type.local() && transformation_type.absolute()) { + if (transformation_type.instance()) { + // ensure that the volume rotates as a rigid body const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); - Matrix3d vol_rotation, vol_scale; - vol_trafo.get_matrix().computeRotationScaling(&vol_rotation, &vol_scale); - const Transform3d trafo = vol_trafo.get_rotation_matrix() * rotation_matrix; - v.set_volume_transformation(vol_trafo.get_offset_matrix() * trafo * Transform3d(vol_scale)); + const Transform3d cached_vol_rotation_matrix = vol_trafo.get_rotation_matrix(); + v.set_volume_transformation(vol_trafo.get_matrix() * cached_vol_rotation_matrix.inverse() * rotation_matrix * cached_vol_rotation_matrix); } else transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center); From 9b4e66f40d20bf8c89ad2d66e4ace8dbb234a091 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 3 Feb 2023 12:06:01 +0100 Subject: [PATCH 10/56] Removed unneded line of code from Selection::get_bounding_box_in_reference_system() --- src/slic3r/GUI/Selection.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index c3b0b57251..4f80ba6a99 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -884,8 +884,6 @@ std::pair Selection::get_bounding_box_in_reference_s }; auto calc_box_size = [point_to_Vec4d, Vec4d_to_Vec3d, apply_transform](const BoundingBoxf3& box, const Transform3d& trafo) { - Geometry::Transformation transformation(trafo); - // box aligned to current reference system std::vector homo_vertices = { point_to_Vec4d({ box.min.x(), box.min.y(), box.min.z() }), From bdd34423468e31da757aa146a91eedbd45bcc11d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 7 Feb 2023 08:58:18 +0100 Subject: [PATCH 11/56] Rework of Selection::get_bounding_box_in_reference_system() --- src/slic3r/GUI/Selection.cpp | 116 ++++++++++------------------------- 1 file changed, 31 insertions(+), 85 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 4f80ba6a99..e31e40eca0 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -836,106 +836,52 @@ const std::pair& Selection::get_bounding_box_in_curr std::pair Selection::get_bounding_box_in_reference_system(ECoordinatesType type) const { - BoundingBoxf3 original_box; + // + // trafo to current reference system + // Transform3d trafo; - - // - // calculate box aligned to current reference system - // switch (type) { - case ECoordinatesType::World: - { - original_box = get_bounding_box(); - trafo = Transform3d::Identity(); - break; - } - case ECoordinatesType::Instance: { - for (unsigned int id : m_list) { - const GLVolume& v = *get_volume(id); - original_box.merge(v.transformed_convex_hull_bounding_box(v.get_volume_transformation().get_matrix())); - } - trafo = get_first_volume()->get_instance_transformation().get_matrix(); - break; - } - case ECoordinatesType::Local: { - assert(is_single_volume_or_modifier() || is_single_volume_instance()); - const GLVolume& v = *get_first_volume(); - original_box = v.bounding_box(); - trafo = v.world_matrix(); - break; - } + case ECoordinatesType::World: { trafo = Transform3d::Identity(); break; } + case ECoordinatesType::Instance: { trafo = get_first_volume()->get_instance_transformation().get_matrix(); break; } + case ECoordinatesType::Local: { trafo = get_first_volume()->world_matrix(); break; } } // - // calculate box size in world coordinates + // trafo basis in world coordinates // - auto point_to_Vec4d = [](const Vec3d& p) { return Vec4d(p.x(), p.y(), p.z(), 1.0); }; - auto Vec4d_to_Vec3d = [](const Vec4d& v) { return Vec3d(v.x(), v.y(), v.z()); }; + const Transform3d basis_trafo = Geometry::Transformation(trafo).get_rotation_matrix(); + std::vector axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; + for (size_t i = 0; i < axes.size(); ++i) { + axes[i] = basis_trafo * axes[i]; + } - auto apply_transform = [](const std::vector& original, const Transform3d& trafo, bool normalize) { - std::vector transformed(original.size()); - for (size_t i = 0; i < original.size(); ++i) { - transformed[i] = trafo * original[i]; - if (normalize) - transformed[i].normalize(); - } - return transformed; - }; - - auto calc_box_size = [point_to_Vec4d, Vec4d_to_Vec3d, apply_transform](const BoundingBoxf3& box, const Transform3d& trafo) { - // box aligned to current reference system - std::vector homo_vertices = { - point_to_Vec4d({ box.min.x(), box.min.y(), box.min.z() }), - point_to_Vec4d({ box.max.x(), box.min.y(), box.min.z() }), - point_to_Vec4d({ box.max.x(), box.max.y(), box.min.z() }), - point_to_Vec4d({ box.min.x(), box.max.y(), box.min.z() }), - point_to_Vec4d({ box.min.x(), box.min.y(), box.max.z() }), - point_to_Vec4d({ box.max.x(), box.min.y(), box.max.z() }), - point_to_Vec4d({ box.max.x(), box.max.y(), box.max.z() }), - point_to_Vec4d({ box.min.x(), box.max.y(), box.max.z() }) - }; - - // box vertices in world coordinates - std::vector transformed_homo_vertices = apply_transform(homo_vertices, trafo, false); - - // project back to current reference system - const std::vector homo_axes = { Vec4d::UnitX(), Vec4d::UnitY(), Vec4d::UnitZ() }; - std::vector transformed_homo_axes = apply_transform(homo_axes, Geometry::Transformation(trafo).get_matrix_no_scaling_factor(), true); - std::vector transformed_axes(transformed_homo_axes.size()); - for (size_t i = 0; i < transformed_homo_axes.size(); ++i) { - transformed_axes[i] = Vec4d_to_Vec3d(transformed_homo_axes[i]); - } - - Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX }; - Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX }; - - for (const Vec4d& v_homo : transformed_homo_vertices) { - const Vec3d v = Vec4d_to_Vec3d(v_homo); + // + // calculate bounding box aligned to trafo basis + // + Vec3d min = { DBL_MAX, DBL_MAX, DBL_MAX }; + Vec3d max = { -DBL_MAX, -DBL_MAX, -DBL_MAX }; + for (unsigned int id : m_list) { + const GLVolume& vol = *get_volume(id); + const Transform3d vol_world_rafo = vol.world_matrix(); + const TriangleMesh* ch = vol.convex_hull(); + for (const stl_vertex& v : ch->its.vertices) { + const Vec3d world_v = vol_world_rafo * v.cast(); for (int i = 0; i < 3; ++i) { - const double dot_i = v.dot(transformed_axes[i]); - min(i) = std::min(min(i), dot_i); - max(i) = std::max(max(i), dot_i); + const double i_comp = world_v.dot(axes[i]); + min(i) = std::min(min(i), i_comp); + max(i) = std::max(max(i), i_comp); } } - - // return size - const Vec3d size = max - min; - return size; - }; - - const Vec3d box_size = calc_box_size(original_box, trafo); - const std::vector box_center = { point_to_Vec4d(original_box.center()) }; - std::vector transformed_box_center = apply_transform(box_center, trafo, false); - - // - // return box centered at 0, 0, 0 - // + } + const Vec3d box_size = max - min; const Vec3d half_box_size = 0.5 * box_size; BoundingBoxf3 out_box(-half_box_size, half_box_size); Geometry::Transformation out_trafo(trafo); - out_trafo.set_offset(Vec4d_to_Vec3d(transformed_box_center[0])); + const Vec3d center = 0.5 * (min + max); + out_trafo.set_offset(basis_trafo * center); return { out_box, out_trafo.get_matrix_no_scaling_factor() }; + } #endif // ENABLE_WORLD_COORDINATE From c843268f7e14982a2bdb51c2370d06e80ded84a7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 7 Feb 2023 09:42:01 +0100 Subject: [PATCH 12/56] Fixed warnings --- src/slic3r/GUI/Selection.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e31e40eca0..dc0242e60c 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -2129,7 +2129,6 @@ void Selection::update_type() unsigned int volumes_count = (unsigned int)model_object->volumes.size(); unsigned int instances_count = (unsigned int)model_object->instances.size(); if (volumes_count * instances_count == 1) { - const ModelVolume* model_volume = model_object->volumes[first->volume_idx()]; m_type = SingleFullObject; // ensures the correct mode is selected m_mode = Instance; @@ -2960,7 +2959,6 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_ const int object_idx = volume_i->object_idx(); const int instance_idx = volume_i->instance_idx(); const Transform3d& curr_inst_trafo_i = volume_i->get_instance_transformation().get_matrix(); - const bool curr_inst_left_handed = is_left_handed(curr_inst_trafo_i); const Transform3d& old_inst_trafo_i = m_cache.volumes_data[i].get_instance_transform().get_matrix(); bool mirrored = is_left_handed(curr_inst_trafo_i) != is_left_handed(old_inst_trafo_i); // bool mirrored = curr_inst_trafo_i.linear().determinant() * old_inst_trafo_i.linear().determinant() < 0; From 9c7a4a1e519fa51c03aa177ad0006e4278e115f8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 7 Feb 2023 11:05:32 +0100 Subject: [PATCH 13/56] Fixed rendering of scale gizmo --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 28 ++++++-------------------- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 3 --- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 173585ec00..42790caf36 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -264,9 +264,8 @@ void GLGizmoScale3D::on_render() #endif // ENABLE_GL_CORE_PROFILE glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); - const Transform3d base_matrix = local_transform(selection); for (int i = 0; i < 10; ++i) { - m_grabbers[i].matrix = base_matrix; + m_grabbers[i].matrix = box_trafo; } const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0); @@ -281,7 +280,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -315,7 +314,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -347,7 +346,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -379,7 +378,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -411,7 +410,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * base_matrix); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -920,20 +919,5 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const return ratio; } -#if ENABLE_WORLD_COORDINATE -Transform3d GLGizmoScale3D::local_transform(const Selection& selection) const -{ - Transform3d ret = Geometry::translation_transform(m_center); - if (!wxGetApp().obj_manipul()->is_world_coordinates()) { - const GLVolume& v = *selection.get_first_volume(); - Transform3d orient_matrix = v.get_instance_transformation().get_rotation_matrix(); - if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) - orient_matrix = orient_matrix * v.get_volume_transformation().get_rotation_matrix(); - ret = ret * orient_matrix; - } - return ret; -} -#endif // ENABLE_WORLD_COORDINATE - } // namespace GUI } // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 96594786e1..fe6ab4972e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -102,9 +102,6 @@ private: void do_scale_uniform(const UpdateData& data); double calc_ratio(const UpdateData& data) const; -#if ENABLE_WORLD_COORDINATE - Transform3d local_transform(const Selection& selection) const; -#endif // ENABLE_WORLD_COORDINATE }; From c2a6694df32bc936cfb3306bd3136111d891c24d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 7 Feb 2023 13:48:13 +0100 Subject: [PATCH 14/56] Fixed constrained scale of single instance selections --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 42790caf36..7e287c6474 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -756,7 +756,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) double ratio = calc_ratio(data); if (ratio > 0.0) { Vec3d curr_scale = m_scale; - Vec3d starting_scale = m_starting.scale; + const Vec3d starting_scale = m_starting.scale; const Selection& selection = m_parent.get_selection(); const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type(); @@ -769,13 +769,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) if (m_hover_id == 2 * axis) local_offset *= -1.0; - Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection) - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from world coordinates to instance coordinates - center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset; - - local_offset += (ratio - 1.0) * center_offset(axis); - switch (axis) { case X: { m_offset = local_offset * Vec3d::UnitX(); break; } @@ -848,14 +841,6 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData & data) if (m_hover_id == 6 || m_hover_id == 7) m_offset.y() *= -1.0; - Vec3d center_offset = m_starting.instance_center - m_starting.center; // world coordinates (== Vec3d::Zero() for single volume selection) - - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from world coordinates to instance coordinates - center_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix().inverse() * center_offset; - - m_offset += (ratio - 1.0) * center_offset; - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) // from instance coordinates to world coordinates m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset; From 0ae0f1e5007052d5318f59cf7d0efba723f431de Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 7 Feb 2023 14:38:36 +0100 Subject: [PATCH 15/56] Fixed scaling objects using sidebar panel while the preview is shown --- src/slic3r/GUI/Selection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index dc0242e60c..a9d06a76ec 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1419,7 +1419,7 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation if (transformation_type.absolute()) { if (m_mode == Instance) { if (is_single_full_instance()) { - BoundingBoxf3 current_box = m_box.get_bounding_box(); + BoundingBoxf3 current_box = get_bounding_box_in_current_reference_system().first; BoundingBoxf3 original_box; if (transformation_type.world()) original_box = get_full_unscaled_instance_bounding_box(); From 217708e812308f8dc7032a2b8ff7ab0f4efbf5c8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 7 Feb 2023 15:44:38 +0100 Subject: [PATCH 16/56] Fixing the transformation resets. --- src/libslic3r/Geometry.cpp | 57 +++++++++++++++----------------------- src/libslic3r/Geometry.hpp | 2 ++ 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index f3b8926d92..1068ffa202 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -723,27 +723,23 @@ void Transformation::reset() void Transformation::reset_rotation() { const Geometry::TransformationSVD svd(*this); - const Transform3d mirror = is_left_handed() ? Geometry::scale_transform({ -1.0, 1.0, 1.0 }) : Transform3d::Identity(); - m_matrix = get_offset_matrix() * mirror * Transform3d(svd.v * svd.s * svd.v.transpose()); + m_matrix = get_offset_matrix() * Transform3d(svd.v * svd.s * svd.v.transpose()) * svd.mirror_matrix(); } void Transformation::reset_scaling_factor() { const Geometry::TransformationSVD svd(*this); - m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * Transform3d(svd.v.transpose()) * svd.mirror_matrix(); } void Transformation::reset_skew() { auto new_scale_factor = [](const Matrix3d& s) { -// return s.determinant(); // scale determinant - return (s(0, 0) + s(1, 1) + s(2, 2)) / 3.0; // scale average -// return std::max(s(0, 0), std::max(s(1, 1), s(2, 2))); // max scale -// return std::min(s(0, 0), std::min(s(1, 1), s(2, 2))); // min scale + return pow(s(0, 0) * s(1, 1) * s(2, 2), 1. / 3.); // scale average }; const Geometry::TransformationSVD svd(*this); - m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()); + m_matrix = get_offset_matrix() * Transform3d(svd.u) * scale_transform(new_scale_factor(svd.s)) * Transform3d(svd.v.transpose()) * svd.mirror_matrix(); } Transform3d Transformation::get_matrix_no_offset() const @@ -843,46 +839,37 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation #if ENABLE_WORLD_COORDINATE TransformationSVD::TransformationSVD(const Transform3d& trafo) { - static const Matrix3d mirror_x = Geometry::scale_transform({ -1.0, 1.0, 1.0 }).linear(); + const auto &m0 = trafo.matrix().block<3, 3>(0, 0); + mirror = m0.determinant() < 0.0; - const Matrix3d m = trafo.matrix().block<3, 3>(0, 0); + Matrix3d m; + if (mirror) + m = m0 * Eigen::DiagonalMatrix(-1.0, 1.0, 1.0); + else + m = m0; const Eigen::JacobiSVD svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV); u = svd.matrixU(); v = svd.matrixV(); s = svd.singularValues().asDiagonal(); - mirror = m.determinant() < 0.0; scale = !s.isApprox(Matrix3d::Identity()); - anisotropic_scale = std::abs(s(0, 0) - s(1, 1)) > EPSILON || std::abs(s(1, 1) - s(2, 2)) > EPSILON; - rotation = mirror ? !u.isApprox(mirror_x) : !v.isApprox(u.transpose()); + anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); + rotation = !v.isApprox(u.transpose()); - rotation_90_degrees = true; - if (rotation) { + if (anisotropic_scale && rotation) { + rotation_90_degrees = true; for (int i = 0; i < 3; ++i) { - const Vec3d row = u.row(i).cwiseAbs(); - if ((std::abs(row[0] - 1.0) > EPSILON && row[1] > EPSILON && row[2] > EPSILON) || - (row[0] > EPSILON && std::abs(row[1] - 1.0) > EPSILON && row[2] > EPSILON) || - (row[0] > EPSILON && row[1] > EPSILON && std::abs(row[2] - 1.0) > EPSILON)) { + const Vec3d row = v.row(i).cwiseAbs(); + size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.); + size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.); + if (num_zeros != 2 || num_ones != 1) { rotation_90_degrees = false; break; } } - } - else - rotation_90_degrees = true; - - skew = false; - if (anisotropic_scale) { - for (int i = 0; i < 3; ++i) { - const Vec3d row = v.row(i).cwiseAbs(); - if ((std::abs(row[0] - 1.0) > EPSILON && row[1] > EPSILON && row[2] > EPSILON) || - (row[0] > EPSILON && std::abs(row[1] - 1.0) > EPSILON && row[2] > EPSILON) || - (row[0] > EPSILON && row[1] > EPSILON && std::abs(row[2] - 1.0) > EPSILON)) { - skew = true; - break; - } - } - } + skew = ! rotation_90_degrees; + } else + skew = false; } #endif // ENABLE_WORLD_COORDINATE diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index f7b08b9b29..1f287f6a7f 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -554,6 +554,8 @@ struct TransformationSVD explicit TransformationSVD(const Transformation& trafo) : TransformationSVD(trafo.get_matrix()) {} explicit TransformationSVD(const Transform3d& trafo); + + Eigen::DiagonalMatrix mirror_matrix() const { return Eigen::DiagonalMatrix(this->mirror ? -1. : 1., 1., 1.); } }; #endif // ENABLE_WORLD_COORDINATE From d51296bca505c1ed773ad9afe26302d67fc66869 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 7 Feb 2023 16:46:57 +0100 Subject: [PATCH 17/56] Follow-up to 217708e812308f8dc7032a2b8ff7ab0f4efbf5c8 Fixing the transformation resets. --- src/libslic3r/Geometry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 1068ffa202..ac4af48283 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -854,9 +854,9 @@ TransformationSVD::TransformationSVD(const Transform3d& trafo) scale = !s.isApprox(Matrix3d::Identity()); anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2)); - rotation = !v.isApprox(u.transpose()); + rotation = !v.isApprox(u); - if (anisotropic_scale && rotation) { + if (anisotropic_scale) { rotation_90_degrees = true; for (int i = 0; i < 3; ++i) { const Vec3d row = v.row(i).cwiseAbs(); From bf48848b00df9c561402bd7aaed9854d2e4bdece Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Feb 2023 10:21:33 +0100 Subject: [PATCH 18/56] Small optimization - Removed unneeded normalization from gizmos Move, Rotate and Scale --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 6 +++--- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 9ef8728873..5e0db668fb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -1108,14 +1108,14 @@ void GLGizmoCut3D::dragging_grabber_z(const GLGizmoBase::UpdateData &data) Vec3d starting_vec = starting_drag_position - starting_box_center; if (starting_vec.norm() != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); + const 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; + const Vec3d inters = data.mouse_ray.a + (starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; // vector from the starting position to the found intersection - Vec3d inters_vec = inters - starting_drag_position; + const Vec3d inters_vec = inters - starting_drag_position; starting_vec.normalize(); // finds projection of the vector along the staring direction diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 7656226b59..98f183b74e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -339,7 +339,7 @@ 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 - const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; // vector from the starting position to the found intersection const Vec3d inters_vec = inters - m_starting_drag_position; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 7e287c6474..baf8816a50 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -888,7 +888,7 @@ double GLGizmoScale3D::calc_ratio(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 - const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; + const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir; // vector from the starting position to the found intersection const Vec3d inters_vec = inters - m_starting.drag_position; From b182675e35c4953c2af1dd243f6581e21cffc8b8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Feb 2023 10:42:01 +0100 Subject: [PATCH 19/56] Removed obsolete code from gizmo scale --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index baf8816a50..f8da7505d2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -777,10 +777,6 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) default: { m_offset = Vec3d::Zero(); break; } } - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from instance coordinates to world coordinates - m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset; - if (selection.is_single_volume_or_modifier()) { if (coordinates_type == ECoordinatesType::Instance) m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset; @@ -841,10 +837,6 @@ void GLGizmoScale3D::do_scale_uniform(const UpdateData & data) if (m_hover_id == 6 || m_hover_id == 7) m_offset.y() *= -1.0; - if (selection.is_single_full_instance() && coordinates_type == ECoordinatesType::Local) - // from instance coordinates to world coordinates - m_offset = selection.get_first_volume()->get_instance_transformation().get_rotation_matrix() * m_offset; - if (selection.is_single_volume_or_modifier()) { if (coordinates_type == ECoordinatesType::Instance) m_offset = selection.get_first_volume()->get_instance_transformation().get_scaling_factor_matrix().inverse() * m_offset; From d6693ea9565e1ba093ec5a48088811b84591e7da Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Feb 2023 14:08:51 +0100 Subject: [PATCH 20/56] Methods Transformation::get_scaling_factor() and Transformation::get_scaling_factor_matrix() modified to use TransformationSVD --- src/libslic3r/Geometry.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index ac4af48283..2c50af6e01 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -539,17 +539,14 @@ void Transformation::set_rotation(Axis axis, double rotation) #if ENABLE_WORLD_COORDINATE Vec3d Transformation::get_scaling_factor() const { - const Transform3d scale = extract_scale(m_matrix); - return { std::abs(scale(0, 0)), std::abs(scale(1, 1)), std::abs(scale(2, 2)) }; + const TransformationSVD svd(*this); + return { svd.s(0, 0), svd.s(1, 1), svd.s(2, 2) }; } Transform3d Transformation::get_scaling_factor_matrix() const { - Transform3d scale = extract_scale(m_matrix); - scale(0, 0) = std::abs(scale(0, 0)); - scale(1, 1) = std::abs(scale(1, 1)); - scale(2, 2) = std::abs(scale(2, 2)); - return scale; + const TransformationSVD svd(*this); + return Transform3d(svd.s); } #endif // ENABLE_WORLD_COORDINATE From 4ad8a5ce8b2e8eb8a0787ef3d30e64b5f1ae29c2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Feb 2023 14:17:22 +0100 Subject: [PATCH 21/56] Removed obsolete code from ObjectManipulation::update_settings_value --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 1d6212ce58..bbae228e06 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -745,7 +745,6 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_rotate_label_string = L("Rotate (relative)"); m_new_position = Vec3d::Zero(); m_new_rotation = Vec3d::Zero(); - m_new_scale = Vec3d(100.0, 100.0, 100.0); m_new_size = selection.get_bounding_box_in_current_reference_system().first.size(); #else m_new_rotation = volume->get_instance_rotation() * (180.0 / M_PI); From 4909bd1b6e389ba33511d1e6efd746f00d53a957 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Feb 2023 14:20:10 +0100 Subject: [PATCH 22/56] Fixed Selection::get_bounding_box_in_reference_system() - Mirrored objects were not accounted properly --- src/slic3r/GUI/Selection.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index a9d06a76ec..8ecb205323 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -850,7 +850,9 @@ std::pair Selection::get_bounding_box_in_reference_s // // trafo basis in world coordinates // - const Transform3d basis_trafo = Geometry::Transformation(trafo).get_rotation_matrix(); + Geometry::Transformation t(trafo); + t.reset_scaling_factor(); + const Transform3d basis_trafo = t.get_matrix_no_offset(); std::vector axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() }; for (size_t i = 0; i < axes.size(); ++i) { axes[i] = basis_trafo * axes[i]; From d319f920cbb36582f424670583c9834aab229d21 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 8 Feb 2023 14:45:45 +0100 Subject: [PATCH 23/56] Simplified code into GLGizmoScale3D::on_render() --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index f8da7505d2..4628745e67 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -218,15 +218,7 @@ void GLGizmoScale3D::on_render() m_bounding_box = box; m_center = box_trafo.translation(); m_grabbers_transform = box_trafo; - m_instance_center = Vec3d::Zero(); - if (selection.is_single_full_instance() && !wxGetApp().obj_manipul()->is_world_coordinates()) - m_instance_center = selection.get_first_volume()->get_instance_offset(); - else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_instance_coordinates()) - m_instance_center = m_center; - else if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) - m_instance_center = m_center; - else - m_instance_center = selection.is_single_full_instance() ? selection.get_first_volume()->get_instance_offset() : m_center; + m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center; // x axis const Vec3d box_half_size = 0.5 * m_bounding_box.size(); From 27fdaef783122c9e6e6f51df19676c2a0064fa1b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 10 Feb 2023 12:50:07 +0100 Subject: [PATCH 24/56] Fixed gizmo rotate in Object Coordinates for mirrored instances --- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 345d733afd..a189600b1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -238,7 +238,13 @@ void GLGizmoRotate::init_data_from_selection(const Selection& selection) selection.get_bounding_box_in_reference_system(ECoordinatesType::Local) : selection.get_bounding_box_in_current_reference_system(); m_bounding_box = box; m_center = box_trafo.translation(); - m_orient_matrix = box_trafo; + m_orient_matrix = Geometry::translation_transform(m_center); + if (!wxGetApp().obj_manipul()->is_world_coordinates()) { + const GLVolume& v = *selection.get_first_volume(); + m_orient_matrix = m_orient_matrix * v.get_instance_transformation().get_rotation_matrix(); + if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) + m_orient_matrix = m_orient_matrix * v.get_volume_transformation().get_rotation_matrix(); + } m_radius = Offset + m_bounding_box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; From 62ecc3a82a4fb13b51c1d942334df434140a3e5a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 10 Feb 2023 14:04:58 +0100 Subject: [PATCH 25/56] Partial revert of 9c7a4a1e519fa51c03aa177ad0006e4278e115f8 which broke gizmo scale in Object Coordinates for mirrored instances --- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 4628745e67..7453f1eb22 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -217,7 +217,13 @@ void GLGizmoScale3D::on_render() const auto& [box, box_trafo] = selection.get_bounding_box_in_current_reference_system(); m_bounding_box = box; m_center = box_trafo.translation(); - m_grabbers_transform = box_trafo; + m_grabbers_transform = Geometry::translation_transform(m_center); + if (!wxGetApp().obj_manipul()->is_world_coordinates()) { + const GLVolume& v = *selection.get_first_volume(); + m_grabbers_transform = m_grabbers_transform * v.get_instance_transformation().get_rotation_matrix(); + if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) + m_grabbers_transform = m_grabbers_transform * v.get_volume_transformation().get_rotation_matrix(); + } m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center; // x axis @@ -257,7 +263,7 @@ void GLGizmoScale3D::on_render() glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); for (int i = 0; i < 10; ++i) { - m_grabbers[i].matrix = box_trafo; + m_grabbers[i].matrix = m_grabbers_transform; } const float grabber_mean_size = (float)((m_bounding_box.size().x() + m_bounding_box.size().y() + m_bounding_box.size().z()) / 3.0); @@ -272,7 +278,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -306,7 +312,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -338,7 +344,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -370,7 +376,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); @@ -402,7 +408,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); const Camera& camera = wxGetApp().plater()->get_camera(); - shader->set_uniform("view_model_matrix", camera.get_view_matrix() * box_trafo); + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * m_grabbers_transform); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); #if ENABLE_GL_CORE_PROFILE const std::array& viewport = camera.get_viewport(); From 7bbd1d189650b50f82c96166fefd0fee61aba2bb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Feb 2023 08:38:30 +0100 Subject: [PATCH 26/56] Fixed crash in Selection::get_bounding_box_in_reference_system() when using modifiers --- src/slic3r/GUI/Selection.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 8ecb205323..a50619e655 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -866,8 +866,11 @@ std::pair Selection::get_bounding_box_in_reference_s for (unsigned int id : m_list) { const GLVolume& vol = *get_volume(id); const Transform3d vol_world_rafo = vol.world_matrix(); - const TriangleMesh* ch = vol.convex_hull(); - for (const stl_vertex& v : ch->its.vertices) { + const TriangleMesh* mesh = vol.convex_hull(); + if (mesh == nullptr) + mesh = &m_model->objects[vol.object_idx()]->volumes[vol.volume_idx()]->mesh(); + assert(mesh != nullptr); + for (const stl_vertex& v : mesh->its.vertices) { const Vec3d world_v = vol_world_rafo * v.cast(); for (int i = 0; i < 3; ++i) { const double i_comp = world_v.dot(axes[i]); From deefe1b2e9f00a2d3f09c6cf692d6a42eac7cb3c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Feb 2023 11:03:30 +0100 Subject: [PATCH 27/56] Fixed rotation of parts when mirror is applied --- src/slic3r/GUI/Selection.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index a50619e655..e2bed03a22 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -998,7 +998,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ assert(transformation_type.relative() || (transformation_type.absolute() && transformation_type.local())); - const Transform3d rotation_matrix = Geometry::rotation_transform(rotation); + Transform3d rotation_matrix = Geometry::rotation_transform(rotation); for (unsigned int i : m_list) { GLVolume& v = *(*m_volumes)[i]; @@ -1027,6 +1027,8 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ // ensure that the volume rotates as a rigid body const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); const Transform3d cached_vol_rotation_matrix = vol_trafo.get_rotation_matrix(); + if ((inst_trafo * vol_trafo).is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) + rotation_matrix = rotation_matrix.inverse(); v.set_volume_transformation(vol_trafo.get_matrix() * cached_vol_rotation_matrix.inverse() * rotation_matrix * cached_vol_rotation_matrix); } else From 646af0256085e462cfe2a477138120f364a01f6e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Feb 2023 12:29:03 +0100 Subject: [PATCH 28/56] Fixed scaling of multipart objects using the sidebar panel --- src/slic3r/GUI/Selection.cpp | 42 +++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index e2bed03a22..099d54fa7a 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1416,31 +1416,29 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation if (!m_valid) return; + Vec3d relative_scale = scale; + if (transformation_type.absolute()) { + // converts to relative scale + if (m_mode == Instance) { + if (is_single_full_instance()) { + BoundingBoxf3 current_box = get_bounding_box_in_current_reference_system().first; + BoundingBoxf3 original_box; + if (transformation_type.world()) + original_box = get_full_unscaled_instance_bounding_box(); + else + original_box = get_full_unscaled_instance_local_bounding_box(); + + relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size()); + } + } + transformation_type.set_relative(); + } + for (unsigned int i : m_list) { GLVolume& v = *(*m_volumes)[i]; const VolumeCache& volume_data = m_cache.volumes_data[i]; const Geometry::Transformation& inst_trafo = volume_data.get_instance_transform(); - Vec3d relative_scale = scale; - - if (transformation_type.absolute()) { - if (m_mode == Instance) { - if (is_single_full_instance()) { - BoundingBoxf3 current_box = get_bounding_box_in_current_reference_system().first; - BoundingBoxf3 original_box; - if (transformation_type.world()) - original_box = get_full_unscaled_instance_bounding_box(); - else - original_box = get_full_unscaled_instance_local_bounding_box(); - - relative_scale = original_box.size().cwiseProduct(scale).cwiseQuotient(current_box.size()); - } - } - else { - } - transformation_type.set_relative(); - } - if (m_mode == Instance) { if (transformation_type.instance()) { const Vec3d world_inst_pivot = m_cache.dragging_center - inst_trafo.get_offset(); @@ -2940,10 +2938,10 @@ static void verify_instances_rotation_synchronized(const Model &model, const GLV assert(idx_volume_first != -1); // object without instances? if (idx_volume_first == -1) continue; - const Transform3d::ConstLinearPart &rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear(); + const Transform3d::ConstLinearPart& rotation0 = volumes[idx_volume_first]->get_instance_transformation().get_matrix().linear(); for (int i = idx_volume_first + 1; i < (int)volumes.size(); ++i) if (volumes[i]->object_idx() == idx_object) { - const Transform3d::ConstLinearPart &rotation = volumes[i]->get_instance_transformation().get_matrix().linear(); + const Transform3d::ConstLinearPart& rotation = volumes[i]->get_instance_transformation().get_matrix().linear(); assert(is_rotation_xy_synchronized(rotation, rotation0)); } } From 8b5b6621645f019328d2b261e5d14afcd06fb662 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Feb 2023 14:03:46 +0100 Subject: [PATCH 29/56] Revert of d6693ea9565e1ba093ec5a48088811b84591e7da --- src/libslic3r/Geometry.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 2c50af6e01..ac4af48283 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -539,14 +539,17 @@ void Transformation::set_rotation(Axis axis, double rotation) #if ENABLE_WORLD_COORDINATE Vec3d Transformation::get_scaling_factor() const { - const TransformationSVD svd(*this); - return { svd.s(0, 0), svd.s(1, 1), svd.s(2, 2) }; + const Transform3d scale = extract_scale(m_matrix); + return { std::abs(scale(0, 0)), std::abs(scale(1, 1)), std::abs(scale(2, 2)) }; } Transform3d Transformation::get_scaling_factor_matrix() const { - const TransformationSVD svd(*this); - return Transform3d(svd.s); + Transform3d scale = extract_scale(m_matrix); + scale(0, 0) = std::abs(scale(0, 0)); + scale(1, 1) = std::abs(scale(1, 1)); + scale(2, 2) = std::abs(scale(2, 2)); + return scale; } #endif // ENABLE_WORLD_COORDINATE From 745c55abc332cbc3bc5092bb0b40abf8638ed216 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Feb 2023 14:53:21 +0100 Subject: [PATCH 30/56] Follow-up of 9c7a4a1e519fa51c03aa177ad0006e4278e115f8 and 62ecc3a82a4fb13b51c1d942334df434140a3e5a - Fixed gizmo scale rotating for parts having skew and its rendering for mirrored parts --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 11 +++++++++-- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 21 +++++---------------- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 322c631514..a884a0589d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -198,16 +198,23 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const } void GLGizmoBase::render_grabbers(float size) const +{ + render_grabbers(0, m_grabbers.size() - 1, size, false); +} + +void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const { GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) return; shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - for (int i = 0; i < (int)m_grabbers.size(); ++i) { + glsafe(::glDisable(GL_CULL_FACE)); + for (size_t i = first; i <= last; ++i) { if (m_grabbers[i].enabled) - m_grabbers[i].render(m_hover_id == i, size); + m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size); } + glsafe(::glEnable(GL_CULL_FACE)); shader->stop_using(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 38d8c26617..108b869da5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -219,6 +219,7 @@ protected: void render_grabbers(const BoundingBoxf3& box) const; void render_grabbers(float size) const; + void render_grabbers(size_t first, size_t last, float size, bool force_hover) const; std::string format(float value, unsigned int decimals) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 7453f1eb22..9e5191f65f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -217,13 +217,7 @@ void GLGizmoScale3D::on_render() const auto& [box, box_trafo] = selection.get_bounding_box_in_current_reference_system(); m_bounding_box = box; m_center = box_trafo.translation(); - m_grabbers_transform = Geometry::translation_transform(m_center); - if (!wxGetApp().obj_manipul()->is_world_coordinates()) { - const GLVolume& v = *selection.get_first_volume(); - m_grabbers_transform = m_grabbers_transform * v.get_instance_transformation().get_rotation_matrix(); - if (selection.is_single_volume_or_modifier() && wxGetApp().obj_manipul()->is_local_coordinates()) - m_grabbers_transform = m_grabbers_transform * v.get_volume_transformation().get_rotation_matrix(); - } + m_grabbers_transform = box_trafo; m_instance_center = (selection.is_single_full_instance() || selection.is_single_volume_or_modifier()) ? selection.get_first_volume()->get_instance_offset() : m_center; // x axis @@ -329,8 +323,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[0].render(true, grabber_mean_size); - m_grabbers[1].render(true, grabber_mean_size); + render_grabbers(0, 1, grabber_mean_size, true); shader->stop_using(); } } @@ -361,8 +354,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[2].render(true, grabber_mean_size); - m_grabbers[3].render(true, grabber_mean_size); + render_grabbers(2, 3, grabber_mean_size, true); shader->stop_using(); } } @@ -393,8 +385,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - m_grabbers[4].render(true, grabber_mean_size); - m_grabbers[5].render(true, grabber_mean_size); + render_grabbers(4, 5, grabber_mean_size, true); shader->stop_using(); } } @@ -428,9 +419,7 @@ void GLGizmoScale3D::on_render() if (shader != nullptr) { shader->start_using(); shader->set_uniform("emission_factor", 0.1f); - for (int i = 6; i < 10; ++i) { - m_grabbers[i].render(true, grabber_mean_size); - } + render_grabbers(6, 9, grabber_mean_size, true); shader->stop_using(); } } From 7c58cf267fbc23a98e6019ce17cdda6043170a2b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Feb 2023 08:25:59 +0100 Subject: [PATCH 31/56] Fixed rotation of parts, in Part Coordinates, when mirror is applied --- src/slic3r/GUI/Selection.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 099d54fa7a..5b44f4fd58 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1031,8 +1031,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ rotation_matrix = rotation_matrix.inverse(); v.set_volume_transformation(vol_trafo.get_matrix() * cached_vol_rotation_matrix.inverse() * rotation_matrix * cached_vol_rotation_matrix); } - else + else { + if (transformation_type.local()) { + const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); + if ((inst_trafo * vol_trafo).is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) + rotation_matrix = rotation_matrix.inverse(); + } transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center); + } } } } From 795d5120bf48576220d71fcdf8b1825e283b0efa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Feb 2023 11:56:40 +0100 Subject: [PATCH 32/56] Fixed missing update of sidebar panel after orienting an object using Plalce on Face gizmo --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index bbae228e06..d7bf147877 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -951,7 +951,7 @@ void ObjectManipulation::update_reset_buttons_visibility() show_drop_to_bed = std::abs(min_z) > EPSILON; const GLVolume* volume = selection.get_first_volume(); - Geometry::Transformation trafo = selection.is_single_full_instance() ? volume->get_instance_transformation() : volume->get_volume_transformation(); + const Geometry::Transformation trafo = selection.is_single_full_instance() ? volume->get_instance_transformation() : volume->get_volume_transformation(); const Geometry::TransformationSVD trafo_svd(trafo); show_rotation = trafo_svd.rotation; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 88803e59d4..947ef2df8e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -3,7 +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" #include "libslic3r/Geometry/ConvexHull.hpp" @@ -38,6 +38,7 @@ bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event) // 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")); + wxGetApp().obj_manipul()->set_dirty(); } return true; } From 4695855f46a073d21770741e6ca2800e905a3ad9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Feb 2023 14:17:45 +0100 Subject: [PATCH 33/56] Fixed synchronization of instances while scaling --- src/slic3r/GUI/Selection.cpp | 13 ++++--------- src/slic3r/GUI/Selection.hpp | 4 ---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 5b44f4fd58..333c7a9ce7 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1047,14 +1047,7 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ if (m_mode == Instance) { int rot_axis_max = 0; rotation.cwiseAbs().maxCoeff(&rot_axis_max); - SyncRotationType synch; - if (transformation_type.world() && rot_axis_max == 2) - synch = SyncRotationType::NONE; - else if (transformation_type.instance()) - synch = SyncRotationType::FULL; - else - synch = SyncRotationType::GENERAL; - synchronize_unselected_instances(synch); + synchronize_unselected_instances((transformation_type.world() && rot_axis_max == 2) ? SyncRotationType::NONE : SyncRotationType::GENERAL); } else if (m_mode == Volume) synchronize_unselected_volumes(); @@ -1483,7 +1476,9 @@ void Selection::scale_and_translate(const Vec3d& scale, const Vec3d& translation #if !DISABLE_INSTANCES_SYNCH if (m_mode == Instance) - synchronize_unselected_instances(SyncRotationType::NONE); + // even if there is no rotation, we pass SyncRotationType::GENERAL to force + // synchronize_unselected_instances() to apply the scale to the other instances + synchronize_unselected_instances(SyncRotationType::GENERAL); else if (m_mode == Volume) synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 29398eabfd..654e3c22dd 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -497,10 +497,6 @@ public: NONE = 0, // Synchronize after rotation by an axis not parallel with Z. GENERAL = 1, -#if ENABLE_WORLD_COORDINATE - // Fully synchronize rotation. - FULL = 2, -#endif // ENABLE_WORLD_COORDINATE }; void synchronize_unselected_instances(SyncRotationType sync_rotation_type); void synchronize_unselected_volumes(); From 44d1e3de675e25fef0a1bfcff44687675622acdc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Feb 2023 08:57:24 +0100 Subject: [PATCH 34/56] Fixed orientation of sequential print clearance contours while dragging instances --- src/slic3r/GUI/GLCanvas3D.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8809624343..e6bf410b76 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3984,13 +3984,8 @@ void GLCanvas3D::update_sequential_clearance() polygons.reserve(instances_count); for (size_t i = 0; i < instance_transforms.size(); ++i) { const auto& instances = instance_transforms[i]; - double rotation_z0 = instances.front()->get_rotation().z(); for (const auto& instance : instances) { - Geometry::Transformation transformation; - const Vec3d& offset = instance->get_offset(); - transformation.set_offset({ offset.x(), offset.y(), 0.0 }); - transformation.set_rotation(Z, instance->get_rotation().z() - rotation_z0); - const Transform3d& trafo = transformation.get_matrix(); + const Transform3d& trafo = instance->get_matrix(); const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i]; Points inst_pts; inst_pts.reserve(hull_2d.size()); From a9ce511304312a19887c8ef7ef8c6da778780268 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 15 Feb 2023 12:48:41 +0100 Subject: [PATCH 35/56] Follow-up of e3cc3be9ab6a047503d0956f76cabffe7f167ffd - Fixes some extra use case where volumes were not rotated as rigid body --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/Selection.cpp | 29 ++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f2a5f3a789..a76fbb764b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3947,8 +3947,8 @@ void GLCanvas3D::update_sequential_clearance() // the results are then cached for following displacements if (m_sequential_print_clearance_first_displacement) { m_sequential_print_clearance.m_hull_2d_cache.clear(); - float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); - double mitter_limit = scale_(0.1); + const float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); + const double mitter_limit = scale_(0.1); m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); for (size_t i = 0; i < m_model->objects.size(); ++i) { ModelObject* model_object = m_model->objects[i]; @@ -3956,7 +3956,7 @@ void GLCanvas3D::update_sequential_clearance() #if ENABLE_WORLD_COORDINATE Geometry::Transformation trafo = model_instance0->get_transformation(); trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() }); - Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), + const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()), // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. shrink_factor, diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 4f051dffe1..fd0a5ec595 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1024,18 +1024,37 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_ } else { if (transformation_type.instance()) { - // ensure that the volume rotates as a rigid body const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); - const Transform3d cached_vol_rotation_matrix = vol_trafo.get_rotation_matrix(); - if ((inst_trafo * vol_trafo).is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) + const Geometry::Transformation world_trafo = inst_trafo * vol_trafo; + // ensure proper sign of rotation for mirrored objects + if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) rotation_matrix = rotation_matrix.inverse(); - v.set_volume_transformation(vol_trafo.get_matrix() * cached_vol_rotation_matrix.inverse() * rotation_matrix * cached_vol_rotation_matrix); + + // ensure that the volume rotates as a rigid body + const Geometry::TransformationSVD world_svd(world_trafo); + if (world_svd.anisotropic_scale) { + const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix(); + rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix; + } + const Transform3d vol_rotation_matrix = vol_trafo.get_rotation_matrix(); + rotation_matrix = vol_rotation_matrix.inverse() * rotation_matrix * vol_rotation_matrix; + + v.set_volume_transformation(vol_trafo.get_matrix() * rotation_matrix); } else { if (transformation_type.local()) { const Geometry::Transformation& vol_trafo = volume_data.get_volume_transform(); - if ((inst_trafo * vol_trafo).is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) + const Geometry::Transformation world_trafo = inst_trafo * vol_trafo; + // ensure proper sign of rotation for mirrored objects + if (world_trafo.is_left_handed() && !rotation.normalized().isApprox(Vec3d::UnitX())) rotation_matrix = rotation_matrix.inverse(); + + // ensure that the volume rotates as a rigid body + const Geometry::TransformationSVD svd(world_trafo); + if (svd.anisotropic_scale) { + const Transform3d vol_scale_matrix = vol_trafo.get_scaling_factor_matrix(); + rotation_matrix = vol_scale_matrix.inverse() * rotation_matrix * vol_scale_matrix; + } } transform_volume_relative(v, volume_data, transformation_type, rotation_matrix, m_cache.dragging_center); } From a17a694604eb872a6b857810054da555648fd3ad Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 6 Jan 2023 09:12:57 +0100 Subject: [PATCH 36/56] Linux specific: Follow-up https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/fdc4ee7542a9a5516dfd6d0c502b5f01ae03c439 - Fix to "Post processigs..." line works like a link --- src/slic3r/GUI/OptionsGroup.cpp | 44 +++++++++++++++++++++++++++------ src/slic3r/GUI/Tab.cpp | 2 -- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 3659a08ccf..688570f904 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1038,25 +1038,50 @@ void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/) void ogStaticText::SetPathEnd(const std::string& link) { +#ifndef __linux__ + + Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) { + SetToolTip(OptionsGroup::get_url(get_app_config()->get("suppress_hyperlinks") != "1" ? link : std::string())); + FocusText(true); + event.Skip(); + }); + Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); }); + Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& event) { if (HasCapture()) return; this->CaptureMouse(); event.Skip(); - } ); + }); Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) { if (!HasCapture()) return; ReleaseMouse(); OptionsGroup::launch_browser(link); event.Skip(); - } ); - Bind(wxEVT_ENTER_WINDOW, [this, link](wxMouseEvent& event) { - SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string())); - FocusText(true); - event.Skip(); }); - Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& event) { FocusText(false); event.Skip(); }); + +#else + + // Workaround: On Linux wxStaticText doesn't receive wxEVT_ENTER(LEAVE)_WINDOW events, + // so implement this behaviour trough wxEVT_MOTION events for this control and it's parent + Bind(wxEVT_MOTION, [link, this](wxMouseEvent& event) { + SetToolTip(OptionsGroup::get_url(!get_app_config()->get_bool("suppress_hyperlinks") ? link : std::string())); + FocusText(true); + event.Skip(); + }); + GetParent()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) { + FocusText(false); + event.Skip(); + }); + + // On Linux a mouse capturing causes a totally application freeze + Bind(wxEVT_LEFT_UP, [link, this](wxMouseEvent& event) { + OptionsGroup::launch_browser(link); + event.Skip(); + }); + +#endif } void ogStaticText::FocusText(bool focus) @@ -1065,7 +1090,10 @@ void ogStaticText::FocusText(bool focus) return; SetFont(focus ? Slic3r::GUI::wxGetApp().link_font() : - Slic3r::GUI::wxGetApp().normal_font()); + Slic3r::GUI::wxGetApp().normal_font()); +#ifdef __linux__ + this->GetContainingSizer()->Layout(); +#endif Refresh(); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9932c6f459..2642cd0042 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1732,9 +1732,7 @@ void TabPrint::update_description_lines() if (m_post_process_explanation) { m_post_process_explanation->SetText( _L("Post processing scripts shall modify G-code file in place.")); -#ifndef __linux__ m_post_process_explanation->SetPathEnd("post-processing-scripts_283913"); -#endif // __linux__ } // upadte G-code substitutions from the current configuration { From fac44c8c5e95c2388da491d0a3d172050335ecb4 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 16 Feb 2023 13:22:09 +0100 Subject: [PATCH 37/56] Fix of crash in config wizard Missing has_section() before get_section() --- src/slic3r/GUI/ConfigWizard.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 2a17df8a13..1cf647ed7f 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -3175,7 +3175,8 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese // apply materials in app_config for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS}) - app_config->set_section(section_name, appconfig_new.get_section(section_name)); + if (appconfig_new.has_section(section_name)) + app_config->set_section(section_name, appconfig_new.get_section(section_name)); app_config->set_vendors(appconfig_new); From db9e6368bddef3dcb0036f06962a66ff11654f2f Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 16 Feb 2023 13:34:57 +0100 Subject: [PATCH 38/56] PrusaLink: getting storages reads also "ro" as it is used instead of "read_only" in PrusaLink 0.7.0RC2 --- src/slic3r/Utils/OctoPrint.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 568f9fe670..f769320146 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -734,11 +734,14 @@ bool PrusaLink::get_storage(wxArrayString& output) const const auto path = section.second.get_optional("path"); const auto space = section.second.get_optional("free_space"); const auto read_only = section.second.get_optional("read_only"); + const auto ro = section.second.get_optional("ro"); // In PrusaLink 0.7.0RC2 "read_only" value is stored under "ro". const auto available = section.second.get_optional("available"); if (path && (!available || *available)) { StorageInfo si; si.name = boost::nowide::widen(*path); - si.read_only = read_only ? *read_only : false; // If read_only is missing, assume it is NOT read only. + // If read_only is missing, assume it is NOT read only. + // si.read_only = read_only ? *read_only : false; // version without "ro" + si.read_only = (read_only ? *read_only : (ro ? *ro : false)); si.free_space = space ? std::stoll(*space) : 1; // If free_space is missing, assume there is free space. storage.emplace_back(std::move(si)); } From c8a0c2e469b21c3c4c030ec09a29f5113fe06b09 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 16 Feb 2023 15:03:04 +0100 Subject: [PATCH 39/56] auto paint tool - disable when print setup is invalid and show dialog window with error. --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index ec993fcd44..9f0d838c3b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -519,6 +519,13 @@ bool GLGizmoFdmSupports::has_backend_supports() void GLGizmoFdmSupports::auto_generate() { + std::string err = wxGetApp().plater()->fff_print().validate(); + if (!err.empty()) { + MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + err, _L("Warning"), wxOK); + dlg.ShowModal(); + return; + } + ModelObject *mo = m_c->selection_info()->model_object(); bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){ return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty(); From b2138dd34866e2dcdf15b4a3c11174edf8f3c608 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 29 Sep 2022 16:51:44 +0200 Subject: [PATCH 40/56] OSX specific: Setting the QoS level to the highest level for TBB worker threads: QOS_CLASS_USER_INTERACTIVE. The one level lower QOS_CLASS_USER_INITIATED makes our tester Filip unhappy, because when slicing tree supports Filip switches to a browser on another display wating for the slicer to finish, while OSX moves the slicing threads to high efficiency low coal burning cores. --- src/libslic3r/Thread.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp index aa710aaefc..924fbe9223 100644 --- a/src/libslic3r/Thread.cpp +++ b/src/libslic3r/Thread.cpp @@ -269,7 +269,8 @@ void set_current_thread_qos() #ifdef __APPLE__ // OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance // cores if available. - pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); + // With QOS_CLASS_USER_INITIATED the worker threads drop priority once slicer loses user focus. + pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0); #endif // __APPLE__ } From edd6c7c9d95aa2f46abe293c9bc89d619ec2491a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 16 Feb 2023 16:23:03 +0100 Subject: [PATCH 41/56] Removed an obscure "clip_multipart_objects" option, which caused more harm than good and it is not compatible with the new layer island Z graph. Partial fix of #9679 --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 45 +++++++++++++----------------- src/libslic3r/PrintConfig.hpp | 1 - src/libslic3r/PrintObject.cpp | 3 +- src/libslic3r/PrintObjectSlice.cpp | 13 ++------- src/slic3r/GUI/Tab.cpp | 3 -- 6 files changed, 24 insertions(+), 43 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 99f6c4cc9e..eebb4d54f8 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -458,7 +458,7 @@ static std::vector s_Preset_print_options { "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects", + "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index c843ec6eda..625b1a5549 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -598,14 +598,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.f)); - def = this->add("clip_multipart_objects", coBool); - def->label = L("Clip multi-part objects"); - def->tooltip = L("When printing multi-material objects, this settings will make Slic3r " - "to clip the overlapping object parts one by the other " - "(2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc)."); - def->mode = comExpert; - def->set_default_value(new ConfigOptionBool(true)); - def = this->add("colorprint_heights", coFloats); def->label = L("Colorprint height"); def->tooltip = L("Heights at which a filament change is to occur."); @@ -4052,6 +4044,22 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionFloat(0.001)); } +// Ignore the following obsolete configuration keys: +static std::set PrintConfigDef_ignore = { + "clip_multipart_objects", + "duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y", + "support_material_tool", "acceleration", "adjust_overhang_flow", + "standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid", + "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", + "seal_position", "vibration_limit", "bed_size", + "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe", + "serial_port", "serial_speed", + // Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed. + "fuzzy_skin_perimeter_mode", "fuzzy_skin_shape", + // Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width. + "wall_add_middle_threshold", "wall_split_middle_threshold", +}; + void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) { // handle legacy options @@ -4125,32 +4133,17 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va } }*/ - // Ignore the following obsolete configuration keys: - static std::set ignore = { - "duplicate_x", "duplicate_y", "gcode_arcs", "multiply_x", "multiply_y", - "support_material_tool", "acceleration", "adjust_overhang_flow", - "standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid", - "start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start", - "seal_position", "vibration_limit", "bed_size", - "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe", - "serial_port", "serial_speed", - // Introduced in some PrusaSlicer 2.3.1 alpha, later renamed or removed. - "fuzzy_skin_perimeter_mode", "fuzzy_skin_shape", - // Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width. - "wall_add_middle_threshold", "wall_split_middle_threshold", - }; - // In PrusaSlicer 2.3.0-alpha0 the "monotonous" infill was introduced, which was later renamed to "monotonic". if (value == "monotonous" && (opt_key == "top_fill_pattern" || opt_key == "bottom_fill_pattern" || opt_key == "fill_pattern")) value = "monotonic"; - if (ignore.find(opt_key) != ignore.end()) { - opt_key = ""; + if (PrintConfigDef_ignore.find(opt_key) != PrintConfigDef_ignore.end()) { + opt_key = {}; return; } if (! print_config_def.has(opt_key)) { - opt_key = ""; + opt_key = {}; return; } } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6e732ce904..b899571490 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -487,7 +487,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, brim_separation)) ((ConfigOptionEnum, brim_type)) ((ConfigOptionFloat, brim_width)) - ((ConfigOptionBool, clip_multipart_objects)) ((ConfigOptionBool, dont_support_bridges)) ((ConfigOptionFloat, elefant_foot_compensation)) ((ConfigOptionFloatOrPercent, extrusion_width)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1e45fb5740..80e26944ae 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -620,8 +620,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "slicing_mode") { steps.emplace_back(posSlice); } else if ( - opt_key == "clip_multipart_objects" - || opt_key == "elefant_foot_compensation" + opt_key == "elefant_foot_compensation" || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 0d950b93ed..9dd74bfd70 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -237,9 +237,6 @@ static std::vector> slices_to_regions( const PrintObjectRegions &print_object_regions, const std::vector &zs, std::vector &&volume_slices, - // If clipping is disabled, then ExPolygons produced by different volumes will never be merged, thus they will be allowed to overlap. - // It is up to the model designer to handle these overlaps. - const bool clip_multipart_objects, const std::function &throw_on_cancel_callback) { model_volumes_sort_by_id(model_volumes); @@ -308,7 +305,7 @@ static std::vector> slices_to_regions( } tbb::parallel_for( tbb::blocked_range(0, zs_complex.size()), - [&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, clip_multipart_objects, &throw_on_cancel_callback] + [&slices_by_region, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, &throw_on_cancel_callback] (const tbb::blocked_range &range) { float z = zs_complex[range.begin()].second; auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z); @@ -359,7 +356,7 @@ static std::vector> slices_to_regions( if (next_region_same_modifier) // To be used in the following iteration. temp_slices[idx_region + 1].expolygons = std::move(source); - } else if ((region.model_volume->is_model_part() && clip_multipart_objects) || region.model_volume->is_negative_volume()) { + } else if (region.model_volume->is_model_part() || region.model_volume->is_negative_volume()) { // Clip every non-zero region preceding it. for (int idx_region2 = 0; idx_region2 < idx_region; ++ idx_region2) if (! temp_slices[idx_region2].expolygons.empty()) { @@ -388,10 +385,7 @@ static std::vector> slices_to_regions( merged = true; } } - // Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility - // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters - // for example. - if (merged && clip_multipart_objects) + if (merged) expolygons = closing_ex(expolygons, float(scale_(EPSILON))); slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); i = j; @@ -696,7 +690,6 @@ void PrintObject::slice_volumes() slice_volumes_inner( print->config(), this->config(), this->trafo_centered(), this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback), - m_config.clip_multipart_objects, throw_on_cancel_callback); for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 2642cd0042..17ddd63abd 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1641,9 +1641,6 @@ void TabPrint::build() optgroup->append_single_option_line("xy_size_compensation"); optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487"); - optgroup = page->new_optgroup(L("Other")); - optgroup->append_single_option_line("clip_multipart_objects"); - optgroup = page->new_optgroup(L("Arachne perimeter generator")); optgroup->append_single_option_line("wall_transition_angle"); optgroup->append_single_option_line("wall_transition_filter_deviation"); From f78a2c0bf662af807bacc938b005279448a1aab9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 16 Feb 2023 16:30:06 +0100 Subject: [PATCH 42/56] Making the Z graph of layer islands more robust for degenerate inputs. Partial fix of #9679 --- src/libslic3r/Layer.cpp | 56 +++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index f86dd5635f..ddf68c46ba 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -105,7 +105,7 @@ static void connect_layer_slices( const coord_t offset_below, const coord_t offset_above #ifndef NDEBUG - , const coord_t offset_end + , const coord_t offset_end #endif // NDEBUG ) { @@ -127,9 +127,7 @@ static void connect_layer_slices( { #ifndef NDEBUG auto assert_intersection_valid = [this](int i, int j) { - assert(i != j); - if (i > j) - std::swap(i, j); + assert(i < j); assert(i >= m_offset_below); assert(i < m_offset_above); assert(j >= m_offset_above); @@ -140,35 +138,47 @@ static void connect_layer_slices( if (polynode.Contour.size() >= 3) { // If there is an intersection point, it should indicate which contours (one from layer below, the other from layer above) intersect. // Otherwise the contour is fully inside another contour. - int32_t i = 0, j = 0; + int32_t i = -1, j = -1; for (int icontour = 0; icontour <= polynode.ChildCount(); ++ icontour) { - const bool first = icontour == 0; - const ClipperLib_Z::Path &contour = first ? polynode.Contour : polynode.Childs[icontour - 1]->Contour; + const ClipperLib_Z::Path &contour = icontour == 0 ? polynode.Contour : polynode.Childs[icontour - 1]->Contour; if (contour.size() >= 3) { - if (first) { - i = contour.front().z(); - j = i; - if (i < 0) { - std::tie(i, j) = m_intersections[-i - 1]; - assert(assert_intersection_valid(i, j)); - goto end; - } - } - for (const ClipperLib_Z::IntPoint& pt : contour) { + for (const ClipperLib_Z::IntPoint &pt : contour) { j = pt.z(); if (j < 0) { - std::tie(i, j) = m_intersections[-j - 1]; - assert(assert_intersection_valid(i, j)); + const auto &intersection = m_intersections[-j - 1]; + assert(intersection.first <= intersection.second); + if (intersection.second < m_offset_above) { + // Ignore intersection of polygons on the 1st layer. + assert(intersection.first >= m_offset_below); + j = i; + } else if (intersection.first >= m_offset_above) { + // Ignore intersection of polygons on the 2nd layer + assert(intersection.second < m_offset_end); + j = i; + } else { + std::tie(i, j) = m_intersections[-j - 1]; + assert(assert_intersection_valid(i, j)); + goto end; + } + } else if (i == -1) { + // First source contour of this expolygon was found. + i = j; + } else if (i != j) { + // Second source contour of this expolygon was found. + if (i > j) + std::swap(i, j); + assert_intersection_valid(i, j); goto end; } - else if (i != j) - goto end; } } } end: bool found = false; - if (i == j) { + if (i == -1) { + // This should not happen. It may only happen if the source contours had just self intersections or intersections with contours at the same layer. + assert(false); + } else if (i == j) { // The contour is completely inside another contour. Point pt(polynode.Contour.front().x(), polynode.Contour.front().y()); if (i < m_offset_above) { @@ -202,8 +212,6 @@ static void connect_layer_slices( } } else { assert(assert_intersection_valid(i, j)); - if (i > j) - std::swap(i, j); i -= m_offset_below; j -= m_offset_above; assert(i >= 0 && i < m_below.lslices_ex.size()); From 43ce4d189457ed0c9e52da68c15c835ac05d89a6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 16 Feb 2023 18:13:55 +0100 Subject: [PATCH 43/56] Follow-up to f78a2c0bf662af807bacc938b005279448a1aab9 --- src/libslic3r/Layer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index ddf68c46ba..d60e077af0 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -167,7 +167,7 @@ static void connect_layer_slices( // Second source contour of this expolygon was found. if (i > j) std::swap(i, j); - assert_intersection_valid(i, j); + assert(assert_intersection_valid(i, j)); goto end; } } From 1dc8ed5610a35ec3dec5d6313b20b1ddff84b9c9 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 1 Feb 2023 10:02:18 +0100 Subject: [PATCH 44/56] Additional checks on filename creation in downloader --- src/slic3r/GUI/DownloaderFileGet.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index 0a1e7ce251..ee407afddd 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -137,13 +137,30 @@ void FileGet::priv::get_perform() std::string extension = boost::filesystem::extension(dest_path); std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size()); std::string final_filename = just_filename; - - size_t version = 0; - while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download"))) + // Find unsed filename + try { + size_t version = 0; + while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)) || boost::filesystem::exists(m_dest_folder / (final_filename + extension + "." + std::to_string(get_current_pid()) + ".download"))) + { + ++version; + if (version > 999) { + wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR); + evt->SetString(GUI::format_wxstr(L"Failed to find suitable filename. Last name: %1%." , (m_dest_folder / (final_filename + extension)).string())); + evt->SetInt(m_id); + m_evt_handler->QueueEvent(evt); + return; + } + final_filename = GUI::format("%1%(%2%)", just_filename, std::to_string(version)); + } + } catch (const boost::filesystem::filesystem_error& e) { - ++version; - final_filename = just_filename + "(" + std::to_string(version) + ")"; + wxCommandEvent* evt = new wxCommandEvent(EVT_DWNLDR_FILE_ERROR); + evt->SetString(e.what()); + evt->SetInt(m_id); + m_evt_handler->QueueEvent(evt); + return; } + m_filename = final_filename + extension; m_tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download"); From ee0871880f0ed5ab34f64812649b25f8b229f727 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 3 Feb 2023 16:43:26 +0100 Subject: [PATCH 45/56] archive dialog dark mode fix --- src/slic3r/GUI/FileArchiveDialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 2b861692a4..7e61e59a06 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -267,6 +267,8 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10); this->SetMinSize(wxSize(80 * em, 30 * em)); this->SetSizer(topSizer); + + wxGetApp().UpdateDlgDarkUI(this, true); } void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect) From 41ab733cd896d794c86865c043baad5d01d640c6 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Feb 2023 15:31:01 +0100 Subject: [PATCH 46/56] Refactoring of DownloaderUtils (remove including ConfigWizard_private.hpp)) --- src/slic3r/GUI/ConfigWizard.cpp | 18 ++++++------- src/slic3r/GUI/ConfigWizard.hpp | 30 +++++++++++++++++++++ src/slic3r/GUI/ConfigWizard_private.hpp | 36 +------------------------ src/slic3r/GUI/GUI_App.cpp | 5 ++-- src/slic3r/GUI/GUI_App.hpp | 4 +-- src/slic3r/GUI/Preferences.cpp | 2 +- src/slic3r/GUI/Preferences.hpp | 2 +- 7 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 1cf647ed7f..0fb9349964 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1315,10 +1315,12 @@ PageUpdate::PageUpdate(ConfigWizard *parent) box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); } + + namespace DownloaderUtils { +namespace { #ifdef _WIN32 - wxString get_downloads_path() { wxString ret; @@ -1330,7 +1332,6 @@ namespace DownloaderUtils CoTaskMemFree(path); return ret; } - #elif __APPLE__ wxString get_downloads_path() { @@ -1348,9 +1349,8 @@ namespace DownloaderUtils } return wxString(); } - #endif - + } Worker::Worker(wxWindow* parent) : wxBoxSizer(wxHORIZONTAL) , m_parent(parent) @@ -1432,16 +1432,16 @@ PageDownloader::PageDownloader(ConfigWizard* parent) ))); #endif - box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->downloader->allow(event.IsChecked()); }); + box_allow_downloads->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->m_downloader->allow(event.IsChecked()); }); - downloader = new DownloaderUtils::Worker(this); - append(downloader); - downloader->allow(box_allow_value); + m_downloader = new DownloaderUtils::Worker(this); + append(m_downloader); + m_downloader->allow(box_allow_value); } bool PageDownloader::on_finish_downloader() const { - return downloader->on_finish(); + return m_downloader->on_finish(); } bool DownloaderUtils::Worker::perform_register(const std::string& path_override/* = {}*/) diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index 88d2e2d7c3..4520dbb8ff 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -14,6 +14,36 @@ class PresetUpdater; namespace GUI { +namespace DownloaderUtils { + class Worker : public wxBoxSizer + { + wxWindow* m_parent{ nullptr }; + wxTextCtrl* m_input_path{ nullptr }; + bool downloader_checked{ false }; +#ifdef __linux__ + bool perform_registration_linux{ false }; +#endif // __linux__ + + void deregister(); + + public: + Worker(wxWindow* parent); + ~Worker() {} + + void allow(bool allow_) { downloader_checked = allow_; } + bool is_checked() const { return downloader_checked; } + wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); } + + void set_path_name(wxString name); + void set_path_name(const std::string& name); + + bool on_finish(); + bool perform_register(const std::string& path_override = {}); +#ifdef __linux__ + bool get_perform_registration_linux() { return perform_registration_linux; } +#endif // __linux__ + }; +} class ConfigWizard: public DPIDialog { diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index c7c6e5152b..832bef46c9 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -418,44 +418,10 @@ struct PageUpdate: ConfigWizardPage PageUpdate(ConfigWizard *parent); }; -namespace DownloaderUtils { - wxString get_downloads_path(); - -class Worker : public wxBoxSizer -{ - wxWindow* m_parent {nullptr}; - wxTextCtrl* m_input_path {nullptr}; - bool downloader_checked {false}; -#ifdef __linux__ - bool perform_registration_linux { false }; -#endif // __linux__ - - void deregister(); - -public: - Worker(wxWindow* parent); - ~Worker(){} - - void allow(bool allow_) { downloader_checked = allow_; } - bool is_checked() const { return downloader_checked; } - wxString path_name() const { return m_input_path ? m_input_path->GetValue() : wxString(); } - - void set_path_name(wxString name); - void set_path_name(const std::string& name); - - bool on_finish(); - bool perform_register(const std::string& path_override = {}); -#ifdef __linux__ - bool get_perform_registration_linux() { return perform_registration_linux; } -#endif // __linux__ -}; - -} - struct PageDownloader : ConfigWizardPage { - DownloaderUtils::Worker* downloader{ nullptr }; + DownloaderUtils::Worker* m_downloader { nullptr }; PageDownloader(ConfigWizard* parent); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a4cfc0cb95..6d49e22f1f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -79,7 +79,6 @@ #include "DesktopIntegrationDialog.hpp" #include "SendSystemInfoDialog.hpp" #include "Downloader.hpp" -#include "ConfigWizard_private.hpp" #include "BitmapCache.hpp" #include "Notebook.hpp" @@ -3081,8 +3080,8 @@ void GUI_App::show_downloader_registration_dialog() ), SLIC3R_APP_NAME, SLIC3R_VERSION) , true, wxYES_NO); if (msg.ShowModal() == wxID_YES) { - auto downloader = new DownloaderUtils::Worker(nullptr); - downloader->perform_register(app_config->get("url_downloader_dest")); + auto downloader_worker = new DownloaderUtils::Worker(nullptr); + downloader_worker->perform_register(app_config->get("url_downloader_dest")); #ifdef __linux__ if (downloader->get_perform_registration_linux()) DesktopIntegrationDialog::perform_desktop_integration(true); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 1a58bd8094..3f8d47fd77 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -306,8 +306,8 @@ public: Plater* plater(); const Plater* plater() const; Model& model(); - NotificationManager * notification_manager(); - GalleryDialog * gallery_dialog(); + NotificationManager* notification_manager(); + GalleryDialog * gallery_dialog(); Downloader* downloader(); // Parameters extracted from the command line to be passed to GUI after initialization. diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 40c5314ee4..0b17043ef0 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -10,7 +10,7 @@ #include "ButtonsDescription.hpp" #include "OG_CustomCtrl.hpp" #include "GLCanvas3D.hpp" -#include "ConfigWizard_private.hpp" +#include "ConfigWizard.hpp" #include diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 15a9576921..4ae93f6f9a 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -59,7 +59,7 @@ class PreferencesDialog : public DPIDialog wxColourPickerCtrl* m_mode_advanced { nullptr }; wxColourPickerCtrl* m_mode_expert { nullptr }; - DownloaderUtils::Worker* downloader{ nullptr }; + DownloaderUtils::Worker* downloader { nullptr }; wxBookCtrlBase* tabs {nullptr}; From 74c34a311556ba0192b2995a1ee78d32a5242247 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Feb 2023 16:57:05 +0100 Subject: [PATCH 47/56] Archive Dialog: Fix of darkmode and resizing followup of cff356ba35aaa9d9fb7780880f60cf1a3e266187 --- src/slic3r/GUI/FileArchiveDialog.cpp | 33 ++++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 7e61e59a06..783c4be3c0 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -172,16 +172,20 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) , m_selected_paths (selected_paths) { +#ifdef _WIN32 + wxGetApp().UpdateDarkUI(this); +#else + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); +#endif + int em = em_unit(); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em)); m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em); m_avc->AppendTextColumn("filename", 1); - std::vector> stack; std::function >&, size_t)> reduce_stack = [] (std::vector>& stack, size_t size) { @@ -233,6 +237,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar } // sorting files will help adjust_stack function to not create multiple same folders std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); }); + size_t entry_count = 0; for (const boost::filesystem::path& path : filtered_entries) { std::shared_ptr parent(nullptr); @@ -242,33 +247,37 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files m_avc->get_model()->AddFile(parent, GUI::format_wxstr(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? + entry_count++; } } + if (entry_count == 1) + on_all_button(); + wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); - wxButton* btn_all = new wxButton(this, wxID_ANY, "All"); + wxButton* btn_all = new wxButton(this, wxID_ANY, _L("All")); btn_all->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_all_button(); }); - btn_sizer->Add(btn_all, 0, wxLeft); + btn_sizer->Add(btn_all, 0); - wxButton* btn_none = new wxButton(this, wxID_ANY, "None"); + wxButton* btn_none = new wxButton(this, wxID_ANY, _L("None")); btn_none->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_none_button(); }); - btn_sizer->Add(btn_none, 0, wxLeft); + btn_sizer->Add(btn_none, 0, wxLEFT, em); btn_sizer->AddStretchSpacer(); - wxButton* btn_run = new wxButton(this, wxID_OK, "Open"); + wxButton* btn_run = new wxButton(this, wxID_OK, _L("Open")); btn_run->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { on_open_button(); }); - btn_sizer->Add(btn_run, 0, wxRIGHT); + btn_sizer->Add(btn_run, 0, wxRIGHT, em); - wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, "Cancel"); + wxButton* cancel_btn = new wxButton(this, wxID_CANCEL, _L("Cancel")); cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& evt) { this->EndModal(wxID_CANCEL); }); - btn_sizer->Add(cancel_btn, 0, wxRIGHT); + btn_sizer->Add(cancel_btn, 0, wxRIGHT, em); topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10); topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10); - this->SetMinSize(wxSize(80 * em, 30 * em)); this->SetSizer(topSizer); - wxGetApp().UpdateDlgDarkUI(this, true); + for (const wxString& id : {_L("All"), _L("None"), _L("Open"), _L("Cancel") }) + wxGetApp().UpdateDarkUI(static_cast(FindWindowByLabel(id, this))); } void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect) From 09b4a88f18f1e94e65f29c6e5b5d475356455375 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 8 Feb 2023 09:41:20 +0100 Subject: [PATCH 48/56] MacOS notification - URL downloader not allowed --- src/slic3r/GUI/GUI_App.cpp | 1 + src/slic3r/GUI/NotificationManager.hpp | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6d49e22f1f..b645593623 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2889,6 +2889,7 @@ void GUI_App::MacOpenURL(const wxString& url) { if (app_config && !app_config->get_bool("downloader_url_registered")) { + notification_manager()->push_notification(NotificationType::URLNotRegistered); BOOST_LOG_TRIVIAL(error) << "Recieved command to open URL, but it is not allowed in app configuration. URL: " << url; return; } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index b3a39a936f..778f99bfa3 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -120,7 +120,9 @@ enum class NotificationType // Short meesage to fill space between start and finish of export ExportOngoing, // Progressbar of download from prusaslicer:// url - URLDownload + URLDownload, + // MacOS specific - PS comes forward even when downloader is not allowed + URLNotRegistered, }; class NotificationManager @@ -916,6 +918,16 @@ private: {NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotificationLevel, 10, _u8L("Undo desktop integration failed.") }, {NotificationType::ExportOngoing, NotificationLevel::RegularNotificationLevel, 0, _u8L("Exporting.") }, + {NotificationType::URLNotRegistered + , NotificationLevel::RegularNotificationLevel + , 10 + , _u8L("PrusaSlicer recieved a download request from Printables.com, but it's not allowed. You can allow it") + , _u8L("here.") + , [](wxEvtHandler* evnthndlr) { + wxGetApp().open_preferences("downloader_url_registered", "Other"); + return true; + } }, + //{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) { // wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }}, //{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") }, From 80c92d1331331b159a102013f0cd8437acc22807 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 8 Feb 2023 10:46:04 +0100 Subject: [PATCH 49/56] missing includes and typos --- src/slic3r/GUI/ConfigWizard.cpp | 6 +++--- src/slic3r/GUI/ConfigWizard.hpp | 4 +++- src/slic3r/GUI/ConfigWizard_private.hpp | 2 -- src/slic3r/GUI/GUI_App.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 0fb9349964..b45d220b8e 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -3035,9 +3035,9 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese #ifdef __linux__ // Desktop integration on Linux - BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->downloader->get_perform_registration_linux(); - if (page_welcome->integrate_desktop() || page_downloader->downloader->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(page_downloader->downloader->get_perform_registration_linux()); + BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux(); + if (page_welcome->integrate_desktop() || page_downloader->m_downloader->get_perform_registration_linux()) + DesktopIntegrationDialog::perform_desktop_integration(page_downloader->m_downloader->get_perform_registration_linux()); #endif // Decide whether to create snapshot based on run_reason and the reset profile checkbox diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index 4520dbb8ff..dcd0297ae9 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include "GUI_Utils.hpp" @@ -17,7 +19,7 @@ namespace GUI { namespace DownloaderUtils { class Worker : public wxBoxSizer { - wxWindow* m_parent{ nullptr }; + wxWindow* m_parent{ nullptr }; wxTextCtrl* m_input_path{ nullptr }; bool downloader_checked{ false }; #ifdef __linux__ diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index 832bef46c9..2dc2c2e233 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -10,12 +10,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b645593623..cdebbb4010 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3084,7 +3084,7 @@ void GUI_App::show_downloader_registration_dialog() auto downloader_worker = new DownloaderUtils::Worker(nullptr); downloader_worker->perform_register(app_config->get("url_downloader_dest")); #ifdef __linux__ - if (downloader->get_perform_registration_linux()) + if (downloader_worker->get_perform_registration_linux()) DesktopIntegrationDialog::perform_desktop_integration(true); #endif // __linux__ } else { From c447fd525501667948dd6d14c7b82e595f535826 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 15 Feb 2023 16:18:02 +0100 Subject: [PATCH 50/56] Archive Dialog minimal size --- src/slic3r/GUI/FileArchiveDialog.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 783c4be3c0..e26e152c59 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -275,6 +275,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar topSizer->Add(m_avc, 1, wxEXPAND | wxALL, 10); topSizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 10); this->SetSizer(topSizer); + SetMinSize(wxSize(40 * em, 30 * em)); for (const wxString& id : {_L("All"), _L("None"), _L("Open"), _L("Cancel") }) wxGetApp().UpdateDarkUI(static_cast(FindWindowByLabel(id, this))); @@ -288,9 +289,8 @@ void FileArchiveDialog::on_dpi_changed(const wxRect& suggested_rect) //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); - + const wxSize& size = wxSize(45 * em, 40 * em); + SetSize(size); //m_tree->Rescale(em); Fit(); From 0ccea3bed915c1c02f39081e337271f0561112bc Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 15 Feb 2023 16:47:16 +0100 Subject: [PATCH 51/56] Archive dialog - size of left column --- src/slic3r/GUI/FileArchiveDialog.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index e26e152c59..7337258cb9 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -182,8 +182,8 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); - m_avc = new ArchiveViewCtrl(this, wxSize(60 * em, 30 * em)); - m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em); + m_avc = new ArchiveViewCtrl(this, wxSize(45 * em, 30 * em)); + wxDataViewColumn* toggle_column = m_avc->AppendToggleColumn(L"\u2714", 0, wxDATAVIEW_CELL_ACTIVATABLE, 6 * em); m_avc->AppendTextColumn("filename", 1); std::vector> stack; @@ -238,11 +238,12 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar // sorting files will help adjust_stack function to not create multiple same folders std::sort(filtered_entries.begin(), filtered_entries.end(), [](const boost::filesystem::path& p1, const boost::filesystem::path& p2){ return p1.string() > p2.string(); }); size_t entry_count = 0; + size_t depth = 1; for (const boost::filesystem::path& path : filtered_entries) { std::shared_ptr parent(nullptr); - adjust_stack(path, stack); + depth = std::max(depth, adjust_stack(path, stack)); if (!stack.empty()) parent = stack.back(); if (std::regex_match(path.extension().string(), pattern_drop)) { // this leaves out non-compatible files @@ -253,6 +254,8 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar if (entry_count == 1) on_all_button(); + toggle_column->SetWidth((4 + depth) * em); + wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); wxButton* btn_all = new wxButton(this, wxID_ANY, _L("All")); From e2762bd7d74cae55f2febd63919ff86d50f3b930 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 15 Feb 2023 16:41:00 +0100 Subject: [PATCH 52/56] Desktop undo registration wont undo downloader registration --- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index a37b76459a..9a71e51b2e 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -435,6 +435,7 @@ void DesktopIntegrationDialog::perform_desktop_integration(bool perform_download "Type=Application\n" "MimeType=x-scheme-handler/prusaslicer;\n" "StartupNotify=false\n" + "NoDisplay=true\n" , name_suffix, version_suffix, excutable_path, version_suffix); // desktop file for downloader as part of main app @@ -487,12 +488,7 @@ void DesktopIntegrationDialog::undo_desktop_intgration() std::remove(path.c_str()); } } - // URL Protocol - path = std::string(app_config->get("desktop_integration_URL_path")); - if (!path.empty()) { - BOOST_LOG_TRIVIAL(debug) << "removing " << path; - std::remove(path.c_str()); - } + // URL Protocol - removed only by undo_downloader_registration now wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess); } From e4ff09a59ffb18e35c3e562baff3ecbbbaa6cd7e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 16 Feb 2023 11:20:17 +0100 Subject: [PATCH 53/56] divide desktop integration and downloader desktop integration --- src/slic3r/GUI/ConfigWizard.cpp | 6 +- src/slic3r/GUI/DesktopIntegrationDialog.cpp | 193 ++++++++++++++++---- src/slic3r/GUI/DesktopIntegrationDialog.hpp | 3 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/Preferences.cpp | 2 +- 5 files changed, 164 insertions(+), 42 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index b45d220b8e..35292d803b 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -3036,8 +3036,10 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese #ifdef __linux__ // Desktop integration on Linux BOOST_LOG_TRIVIAL(debug) << "ConfigWizard::priv::apply_config integrate_desktop" << page_welcome->integrate_desktop() << " perform_registration_linux " << page_downloader->m_downloader->get_perform_registration_linux(); - if (page_welcome->integrate_desktop() || page_downloader->m_downloader->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(page_downloader->m_downloader->get_perform_registration_linux()); + if (page_welcome->integrate_desktop()) + DesktopIntegrationDialog::perform_desktop_integration(); + if (page_downloader->m_downloader->get_perform_registration_linux()) + DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif // Decide whether to create snapshot based on run_reason and the reset profile checkbox diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index 9a71e51b2e..62e8411533 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -218,9 +218,9 @@ bool DesktopIntegrationDialog::integration_possible() { return true; } -void DesktopIntegrationDialog::perform_desktop_integration(bool perform_downloader) +void DesktopIntegrationDialog::perform_desktop_integration() { - BOOST_LOG_TRIVIAL(debug) << "performing desktop integration. With downloader integration: " << perform_downloader; + BOOST_LOG_TRIVIAL(debug) << "performing desktop integration."; // Path to appimage const char *appimage_env = std::getenv("APPIMAGE"); std::string excutable_path; @@ -423,39 +423,6 @@ void DesktopIntegrationDialog::perform_desktop_integration(bool perform_download show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully.")); } } - - if (perform_downloader) - { - std::string desktop_file_downloader = GUI::format( - "[Desktop Entry]\n" - "Name=PrusaSlicer URL Protocol%1%\n" - "Exec=\"%3%\" --single-instance %%u\n" - "Icon=PrusaSlicer%4%\n" - "Terminal=false\n" - "Type=Application\n" - "MimeType=x-scheme-handler/prusaslicer;\n" - "StartupNotify=false\n" - "NoDisplay=true\n" - , name_suffix, version_suffix, excutable_path, version_suffix); - - // desktop file for downloader as part of main app - std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); - if (create_desktop_file(desktop_path, desktop_file_downloader)) { - // save path to desktop file - app_config->set("desktop_integration_URL_path", desktop_path); - // finish registration on mime type - std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix); - BOOST_LOG_TRIVIAL(debug) << "system command: " << command; - int r = system(command.c_str()); - BOOST_LOG_TRIVIAL(debug) << "system result: " << r; - - } else { - BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create URL Protocol desktop file"; - show_error(nullptr, _L("Performing desktop integration failed - could not create URL Protocol desktop file.")); - return; - } - } - wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); } void DesktopIntegrationDialog::undo_desktop_intgration() @@ -488,10 +455,162 @@ void DesktopIntegrationDialog::undo_desktop_intgration() std::remove(path.c_str()); } } - // URL Protocol - removed only by undo_downloader_registration now wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess); } +void DesktopIntegrationDialog::perform_downloader_desktop_integration() +{ + BOOST_LOG_TRIVIAL(debug) << "performing downloader desktop integration."; + // Path to appimage + const char* appimage_env = std::getenv("APPIMAGE"); + std::string excutable_path; + if (appimage_env) { + try { + excutable_path = boost::filesystem::canonical(boost::filesystem::path(appimage_env)).string(); + } + catch (std::exception&) { + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path."; + show_error(nullptr, _L("Performing downloader desktop integration failed - boost::filesystem::canonical did not return appimage path.")); + return; + } + } + else { + // not appimage - find executable + excutable_path = boost::dll::program_location().string(); + //excutable_path = wxStandardPaths::Get().GetExecutablePath().string(); + BOOST_LOG_TRIVIAL(debug) << "non-appimage path to executable: " << excutable_path; + if (excutable_path.empty()) + { + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - no executable found."; + show_error(nullptr, _L("Performing downloader desktop integration failed - Could not find executable.")); + return; + } + } + // Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path' + //boost::replace_all(excutable_path, "'", "'\\''"); + excutable_path = escape_string(excutable_path); + + // Find directories icons and applications + // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. + // If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. + // $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. + // The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. + // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. + std::vectortarget_candidates; + resolve_path_from_var("XDG_DATA_HOME", target_candidates); + resolve_path_from_var("XDG_DATA_DIRS", target_candidates); + + AppConfig* app_config = wxGetApp().app_config; + // suffix string to create different desktop file for alpha, beta. + + std::string version_suffix; + std::string name_suffix; + std::string version(SLIC3R_VERSION); + if (version.find("alpha") != std::string::npos) + { + version_suffix = "-alpha"; + name_suffix = " - alpha"; + } + else if (version.find("beta") != std::string::npos) + { + version_suffix = "-beta"; + name_suffix = " - beta"; + } + + // theme path to icon destination + std::string icon_theme_path; + std::string icon_theme_dirs; + + if (platform_flavor() == PlatformFlavor::LinuxOnChromium) { + icon_theme_path = "hicolor/96x96/apps/"; + icon_theme_dirs = "/hicolor/96x96/apps"; + } + + std::string target_dir_desktop; + + // desktop file + // iterate thru target_candidates to find applications folder + + std::string desktop_file_downloader = GUI::format( + "[Desktop Entry]\n" + "Name=PrusaSlicer URL Protocol%1%\n" + "Exec=\"%2%\" --single-instance %%u\n" + "Terminal=false\n" + "Type=Application\n" + "MimeType=x-scheme-handler/prusaslicer;\n" + "StartupNotify=false\n" + "NoDisplay=true\n" + , name_suffix, excutable_path); + + // desktop file for downloader as part of main app + std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); + if (create_desktop_file(desktop_path, desktop_file_downloader)) { + // save path to desktop file + app_config->set("desktop_integration_URL_path", desktop_path); + // finish registration on mime type + std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix); + BOOST_LOG_TRIVIAL(debug) << "system command: " << command; + int r = system(command.c_str()); + BOOST_LOG_TRIVIAL(debug) << "system result: " << r; + + } + + bool candidate_found = false; + for (size_t i = 0; i < target_candidates.size(); ++i) { + if (contains_path_dir(target_candidates[i], "applications")) { + target_dir_desktop = target_candidates[i]; + // Write slicer desktop file + std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); + if (create_desktop_file(path, desktop_file_downloader)) { + app_config->set("desktop_integration_URL_path", path); + candidate_found = true; + BOOST_LOG_TRIVIAL(debug) << "PrusaSlicerURLProtocol.desktop file installation success."; + break; + } + else { + // write failed - try another path + BOOST_LOG_TRIVIAL(debug) << "Attempt to PrusaSlicerURLProtocol.desktop file installation failed. failed path: " << target_candidates[i]; + target_dir_desktop.clear(); + } + } + } + // if all failed - try creating default home folder + if (!candidate_found) { + // create $HOME/.local/share + create_path(boost::nowide::narrow(wxFileName::GetHomeDir()), ".local/share/applications"); + // create desktop file + target_dir_desktop = GUI::format("%1%/.local/share", wxFileName::GetHomeDir()); + std::string path = GUI::format("%1%/applications/PrusaSlicerURLProtocol%2%.desktop", target_dir_desktop, version_suffix); + if (contains_path_dir(target_dir_desktop, "applications")) { + if (!create_desktop_file(path, desktop_file_downloader)) { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed - could not create desktop file."; + return; + } + app_config->set("desktop_integration_URL_path", path); + } + else { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found."; + return; + } + } + assert(!target_dir_desktop.empty()); + if (target_dir_desktop.empty()) { + // Desktop file not written - end desktop integration + BOOST_LOG_TRIVIAL(error) << "Performing downloader desktop integration failed because the application directory was not found."; + show_error(nullptr, _L("Performing downloader desktop integration failed because the application directory was not found.")); + return; + } + + // finish registration on mime type + std::string command = GUI::format("xdg-mime default PrusaSlicerURLProtocol%1%.desktop x-scheme-handler/prusaslicer", version_suffix); + BOOST_LOG_TRIVIAL(debug) << "system command: " << command; + int r = system(command.c_str()); + BOOST_LOG_TRIVIAL(debug) << "system result: " << r; + + wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess); +} void DesktopIntegrationDialog::undo_downloader_registration() { const AppConfig *app_config = wxGetApp().app_config; @@ -528,7 +647,7 @@ DesktopIntegrationDialog::DesktopIntegrationDialog(wxWindow *parent) wxButton *btn_perform = new wxButton(this, wxID_ANY, _L("Perform")); btn_szr->Add(btn_perform, 0, wxALL, 10); - btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(false); EndModal(wxID_ANY); }); + btn_perform->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { DesktopIntegrationDialog::perform_desktop_integration(); EndModal(wxID_ANY); }); if (can_undo){ wxButton *btn_undo = new wxButton(this, wxID_ANY, _L("Undo")); diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.hpp b/src/slic3r/GUI/DesktopIntegrationDialog.hpp index 08c984083b..19cff1fd85 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.hpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.hpp @@ -29,10 +29,11 @@ public: // if perform_downloader: // Creates Destktop files for PrusaSlicer downloader feature // Regiters PrusaSlicer to start on prusaslicer:// URL - static void perform_desktop_integration(bool perform_downloader); + static void perform_desktop_integration(); // Deletes Desktop files and icons for both PrusaSlicer and GcodeViewer at paths stored in App Config. static void undo_desktop_intgration(); + static void perform_downloader_desktop_integration(); static void undo_downloader_registration(); private: diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index cdebbb4010..520f67aed1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3085,7 +3085,7 @@ void GUI_App::show_downloader_registration_dialog() downloader_worker->perform_register(app_config->get("url_downloader_dest")); #ifdef __linux__ if (downloader_worker->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(true); + DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif // __linux__ } else { app_config->set("downloader_url_registered", "0"); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 0b17043ef0..a47e6065f2 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -712,7 +712,7 @@ void PreferencesDialog::accept(wxEvent&) return; #ifdef __linux__ if( downloader->get_perform_registration_linux()) - DesktopIntegrationDialog::perform_desktop_integration(true); + DesktopIntegrationDialog::perform_downloader_desktop_integration(); #endif // __linux__ } From dc0275f70d4c5d163e1906828cfc08ee46b6d081 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 16 Feb 2023 16:22:56 +0100 Subject: [PATCH 54/56] Cut bug fixing: * Fix for https://dev.prusa3d.com/browse/SPE-1489 - The text is misaligned in cut dialog. * Fix for https://dev.prusa3d.com/browse/SPE-1382 - Cut Plane does not move smoothly for multipart models. + Code cleanup - unused code is removed. --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 78 ++++++++++------------------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 6 ++- 2 files changed, 30 insertions(+), 54 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 194f9c6f83..e87978a22a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -244,7 +244,7 @@ std::string GLGizmoCut3D::get_tooltip() const if (m_hover_id == Z || (m_dragging && m_hover_id == CutPlane)) { 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(m_plane_center); + const BoundingBoxf3& tbb = m_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") + ")"; @@ -401,7 +401,7 @@ bool GLGizmoCut3D::is_looking_forward() const void GLGizmoCut3D::update_clipper() { - BoundingBoxf3 box = bounding_box(); + BoundingBoxf3 box = m_bounding_box; // update cut_normal Vec3d beg, end = beg = m_plane_center; @@ -549,7 +549,7 @@ bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& v return !is_approx(old_val, value); }; - const BoundingBoxf3 bbox = bounding_box(); + const BoundingBoxf3 bbox = m_bounding_box; const float mean_size = float((bbox.size().x() + bbox.size().y() + bbox.size().z()) / 9.0) * (m_imperial_units ? f_mm_to_in : 1.f); ImGuiWrapper::text(label); @@ -795,7 +795,7 @@ void GLGizmoCut3D::render_cut_plane_grabbers() const Transform3d view_matrix = wxGetApp().plater()->get_camera().get_view_matrix() * translation_transform(m_plane_center) * m_rotation_m; - const double mean_size = get_grabber_mean_size(bounding_box()); + const double mean_size = get_grabber_mean_size(m_bounding_box); double size; const bool dragging_by_cut_plane = m_dragging && m_hover_id == CutPlane; @@ -1033,7 +1033,7 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform() else if (!cut_line_processing()){ const Transform3d trafo = translation_transform(m_plane_center) * m_rotation_m; - const BoundingBoxf3 box = bounding_box(); + const BoundingBoxf3 box = m_bounding_box; const double size = get_half_size(get_grabber_mean_size(box)); Vec3d scale = Vec3d(0.75 * size, 0.75 * size, 1.8 * size); @@ -1183,7 +1183,11 @@ void GLGizmoCut3D::dragging_grabber_xy(const GLGizmoBase::UpdateData &data) Vec3d rotation = Vec3d::Zero(); rotation[m_hover_id] = theta; - m_rotation_m = m_start_dragging_m * rotation_transform(rotation); + + const Transform3d rotation_tmp = m_start_dragging_m * rotation_transform(rotation); + if (m_rotation_m.rotation() != rotation_tmp.rotation()) + m_transformed_bounding_box = transformed_bounding_box(m_plane_center); + m_rotation_m = rotation_tmp; m_angle = theta; while (m_angle > two_pi) @@ -1245,9 +1249,13 @@ void GLGizmoCut3D::on_stop_dragging() void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false*/) { + if (m_plane_center == center_pos) + return; + bool can_set_center_pos = force; + BoundingBoxf3 tbb; if (!can_set_center_pos) { - const BoundingBoxf3 tbb = transformed_bounding_box(center_pos); + tbb = transformed_bounding_box(center_pos); if (tbb.max.z() > -1. && tbb.min.z() < 1.) can_set_center_pos = true; else { @@ -1260,6 +1268,7 @@ void GLGizmoCut3D::set_center_pos(const Vec3d& center_pos, bool force/* = false* } if (can_set_center_pos) { + m_transformed_bounding_box = tbb; m_plane_center = center_pos; m_center_offset = m_plane_center - m_bb_center; } @@ -1279,7 +1288,7 @@ BoundingBoxf3 GLGizmoCut3D::bounding_box() const return ret; } -BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, bool revert_move /*= false*/) const +BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center) const { // #ysFIXME !!! BoundingBoxf3 ret; @@ -1299,10 +1308,7 @@ BoundingBoxf3 GLGizmoCut3D::transformed_bounding_box(const Vec3d& plane_center, Vec3d cut_center_offset = plane_center - instance_offset; cut_center_offset[Z] -= sel_info->get_sla_shift(); - const auto move = translation_transform(-cut_center_offset); - const auto move2 = translation_transform(plane_center); - - const auto cut_matrix = (revert_move ? move2 : Transform3d::Identity()) * m_rotation_m.inverse() * move; + const auto cut_matrix = Transform3d::Identity() * m_rotation_m.inverse() * translation_transform(-cut_center_offset); const Selection& selection = m_parent.get_selection(); const Selection::IndicesList& idxs = selection.get_volume_idxs(); @@ -1335,6 +1341,8 @@ bool GLGizmoCut3D::update_bb() const BoundingBoxf3 box = bounding_box(); if (m_max_pos != box.max || m_min_pos != box.min) { + m_bounding_box = box; + invalidate_cut_plane(); m_max_pos = box.max; @@ -1388,7 +1396,7 @@ void GLGizmoCut3D::init_picking_models() } if (!m_plane.model.is_initialized() && !m_hide_cut_plane && !m_connectors_editing) { - const double cp_width = 0.02 * get_grabber_mean_size(bounding_box()); + const double cp_width = 0.02 * get_grabber_mean_size(m_bounding_box); indexed_triangle_set its = its_make_frustum_dowel((double)m_cut_plane_radius_koef * m_radius, cp_width, m_cut_plane_as_circle ? 180 : 4); m_plane.model.init_from(its); m_plane.mesh_raycaster = std::make_unique(std::make_shared(std::move(its))); @@ -1631,9 +1639,8 @@ 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(m_plane_center); - Vec3d tbb_sz = tbb.size(); + Vec3d tbb_sz = m_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; @@ -1646,7 +1653,7 @@ void GLGizmoCut3D::render_build_size() void GLGizmoCut3D::reset_cut_plane() { - set_center(bounding_box().center()); + set_center(m_bb_center); m_rotation_m = Transform3d::Identity(); m_angle_arc.reset(); update_clipper(); @@ -1746,7 +1753,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) const bool has_connectors = !connectors.empty(); - const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && bounding_box().center() == m_plane_center; + const bool is_cut_plane_init = m_rotation_m.isApprox(Transform3d::Identity()) && m_bb_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(); @@ -1838,10 +1845,9 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors) add_vertical_scaled_interval(0.75f); m_imgui->disabled_begin(has_connectors); - add_horizontal_shift(m_imgui->scaled(/*1*/.2f)); ImGuiWrapper::text(_L("Cut to") + ":"); - ImGui::SameLine(); + add_horizontal_scaled_interval(1.2f); if (m_imgui->radio_button(_L("Objects"), !m_keep_as_parts)) m_keep_as_parts = false; ImGui::SameLine(); @@ -1990,38 +1996,6 @@ void GLGizmoCut3D::on_render_input_window(float x, float y, float bottom_limit) render_debug_input_window(x); } -// 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); -#if ENABLE_WORLD_COORDINATE - const Transform3d connector_trafo = is_prizm_dowel ? - Geometry::translation_transform(-m_connector_depth_ratio * Vec3d::UnitZ()) * m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, 2 * m_connector_depth_ratio }) : - m_rotation_m * Geometry::scale_transform({ 0.5 * m_connector_size, 0.5 * m_connector_size, m_connector_depth_ratio }); - -#else - const Transform3d connector_trafo = assemble_transform( - is_prizm_dowel ? Vec3d(0.0, 0.0, -m_connector_depth_ratio) : Vec3d::Zero(), - 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()); -#endif // ENABLE_WORLD_COORDINATE - 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 translation_transform(offset) * scale_transform(Vec3d::Ones() - border_scale) * vol_matrix; -} - bool GLGizmoCut3D::is_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos) { // check if connector pos is out of clipping plane @@ -2072,7 +2046,7 @@ bool GLGizmoCut3D::is_conflict_for_connector(size_t idx, const CutConnectors& co const BoundingBoxf3 cur_tbb = m_shapes[cur_connector.attribs].model.get_bounding_box().transformed(matrix); // check if connector's bounding box is inside the object's bounding box - if (!bounding_box().contains(cur_tbb)) { + if (!m_bounding_box.contains(cur_tbb)) { m_info_stats.outside_bb++; return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 3b7663171a..569122d4ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -43,6 +43,9 @@ class GLGizmoCut3D : public GLGizmoBase Vec3d m_bb_center{ Vec3d::Zero() }; Vec3d m_center_offset{ Vec3d::Zero() }; + BoundingBoxf3 m_bounding_box; + BoundingBoxf3 m_transformed_bounding_box; + // values from RotationGizmo double m_radius{ 0.0 }; double m_grabber_radius{ 0.0 }; @@ -193,7 +196,7 @@ public: void invalidate_cut_plane(); BoundingBoxf3 bounding_box() const; - BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center, bool revert_move = false) const; + BoundingBoxf3 transformed_bounding_box(const Vec3d& plane_center) const; protected: bool on_init() override; @@ -263,7 +266,6 @@ private: void render_connect_mode_radio_button(CutConnectorMode mode); 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_outside_of_cut_contour(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); bool is_conflict_for_connector(size_t idx, const CutConnectors& connectors, const Vec3d cur_pos); void render_connectors(); From b5b548a140c2f61b93210df759c27f6ecea9abfb Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Feb 2023 09:53:22 +0100 Subject: [PATCH 55/56] automatic painting: fix error message on windows when converting from std string to wxstring - use from_u8 --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 9f0d838c3b..aeba4aa814 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -521,7 +521,7 @@ void GLGizmoFdmSupports::auto_generate() { std::string err = wxGetApp().plater()->fff_print().validate(); if (!err.empty()) { - MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + err, _L("Warning"), wxOK); + MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + from_u8(err), _L("Warning"), wxOK); dlg.ShowModal(); return; } From 08135550e27468e6f2e4a5daa884c574559a6760 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Wed, 1 Feb 2023 09:35:18 +0100 Subject: [PATCH 56/56] Fix of hints arrow for gizmos and hints text --- resources/data/hints.ini | 4 ++-- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/data/hints.ini b/resources/data/hints.ini index a418c6a890..af0e476e19 100644 --- a/resources/data/hints.ini +++ b/resources/data/hints.ini @@ -237,14 +237,14 @@ documentation_link = https://help.prusa3d.com/article/prusaslicer-printables-com weight = 3 [hint:Cut tool] -text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation. +text = Cut tool\nDid you know that you can cut a model at any angle and even create aligning pins with the updated Cut tool? Learn more in the documentation. documentation_link = https://help.prusa3d.com/article/cut-tool_1779 hypertext_type = gizmo hypertext_gizmo_item = cut weight = 3 [hint:Measurement tool] -text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation. +text = Measurement tool\nDid you know that you can measure the distances between points, edges and planes, the radius of a hole or the angle between edges or planes? Learn more in the documentation. documentation_link = https://help.prusa3d.com/article/measurement-tool_399451 hypertext_type = gizmo hypertext_gizmo_item = measure diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a76fbb764b..299778be5b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7055,7 +7055,7 @@ void GLCanvas3D::GizmoHighlighter::init(GLGizmosManager* manager, GLGizmosManage { if (m_timer.IsRunning()) invalidate(); - if (!gizmo || !canvas) + if (gizmo == GLGizmosManager::EType::Undefined || !canvas) return; m_timer.Start(300, false); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 21a1f9ba5a..d7d6f309da 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -740,6 +740,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t const float icons_size_x = 2.0f * m_layout.scaled_icons_size() * inv_cnv_w; const float icons_size_y = 2.0f * m_layout.scaled_icons_size() * inv_cnv_h; const float stride_y = 2.0f * m_layout.scaled_stride_y() * inv_cnv_h; + top_y -= stride_y; for (size_t idx : selectable_idxs) { if (idx == highlighted_type) {