From 11b994a7021fc949ed9e18fa422648f7c7c7c457 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 27 Jul 2021 16:17:12 +0200 Subject: [PATCH 1/4] Rotfinder fixes wip --- src/libslic3r/SLA/Rotfinder.cpp | 74 ++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index b84921279..7c1e0daef 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -58,29 +58,6 @@ T sum_score(AccessFn &&accessfn, size_t facecount, size_t Nthreads) return execution::reduce(ex_tbb, from, to, initv, mergefn, accessfn, grainsize); } -// Try to guess the number of support points needed to support a mesh -double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr) -{ - if (mesh.its.vertices.empty()) return std::nan(""); - - auto accessfn = [&mesh, &tr](size_t fi) { - auto triangle = get_transformed_triangle(mesh, tr, fi); - Vec3f U = triangle[1] - triangle[0]; - Vec3f V = triangle[2] - triangle[0]; - Vec3f C = U.cross(V); - - // We should score against the alignment with the reference planes - return scaled(std::abs(C.dot(Vec3f::UnitX())) + - std::abs(C.dot(Vec3f::UnitY()))); - }; - - size_t facecount = mesh.its.indices.size(); - size_t Nthreads = std::thread::hardware_concurrency(); - double S = unscaled(sum_score(accessfn, facecount, Nthreads)); - - return S / facecount; -} - // Get area and normal of a triangle struct Facestats { Vec3f normal; @@ -96,18 +73,43 @@ struct Facestats { } }; +// Try to guess the number of support points needed to support a mesh +double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr) +{ + if (mesh.its.vertices.empty()) return std::nan(""); + + auto accessfn = [&mesh, &tr](size_t fi) { + Facestats fc{get_transformed_triangle(mesh, tr, fi)}; + + float score = fc.area + * (std::abs(fc.normal.dot(Vec3f::UnitX())) + + std::abs(fc.normal.dot(Vec3f::UnitY())) + + std::abs(fc.normal.dot(Vec3f::UnitZ()))); + + // We should score against the alignment with the reference planes + return scaled(score); + }; + + size_t facecount = mesh.its.indices.size(); + size_t Nthreads = std::thread::hardware_concurrency(); + double S = unscaled(sum_score(accessfn, facecount, Nthreads)); + + return S / facecount; +} + // The score function for a particular face inline double get_supportedness_score(const Facestats &fc) { // Simply get the angle (acos of dot product) between the face normal and // the DOWN vector. - float phi = 1. - std::acos(fc.normal.dot(DOWN)) / float(PI); + float cosphi = fc.normal.dot(DOWN); + float phi = 1.f - std::acos(cosphi) / float(PI); // Only consider faces that have slopes below 90 deg: - phi = phi * (phi >= 0.5f); + phi = (1.f + phi) * (phi >= 0.5f); // Make the huge slopes more significant than the smaller slopes - phi = phi * phi * phi; + phi = phi * phi; // Multiply with the area of the current face return fc.area * POINTS_PER_UNIT_AREA * phi; @@ -121,7 +123,7 @@ double get_supportedness_score(const TriangleMesh &mesh, const Transform3f &tr) auto accessfn = [&mesh, &tr](size_t fi) { Facestats fc{get_transformed_triangle(mesh, tr, fi)}; - return get_supportedness_score(fc); + return scaled(get_supportedness_score(fc)); }; size_t facecount = mesh.its.indices.size(); @@ -164,7 +166,7 @@ float get_supportedness_onfloor_score(const TriangleMesh &mesh, Facestats fc{tri}; if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl) - return -fc.area * POINTS_PER_UNIT_AREA; + return -2 * fc.area * POINTS_PER_UNIT_AREA; return get_supportedness_score(fc); }; @@ -353,6 +355,15 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, TriangleMesh mesh = mo.raw_mesh(); mesh.require_shared_vertices(); + ModelInstance* mi = mo.instances[0]; + Vec3d rotation = mi->get_rotation(); + Transform3d trafo_instance = Geometry::assemble_transform(mi->get_offset().z() * Vec3d::UnitZ(), + rotation, + mi->get_scaling_factor(), + mi->get_mirror()); + + mesh.transform(trafo_instance); + // To keep track of the number of iterations unsigned status = 0; @@ -419,6 +430,13 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, // Save the result rot = result.optimum; + std::cout << "Score was: " << result.score << std::endl; + +//auto rt = mo.instances[0]->get_rotation(); +//double score = get_supportedness_score(mesh, to_transform3f({rt(0), rt(1)})); +// std::cout << "Score was: " << score << std::endl; +// rot[0] = rt(0); +// rot[1] = rt(1); } return {rot[0], rot[1]}; From 8b5a63eaf8f42f18bf27d3365ed8c23a4c59a22b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Aug 2021 11:29:09 +0200 Subject: [PATCH 2/4] Make the input mesh transformation consistent. Also add some comments --- src/libslic3r/SLA/Rotfinder.cpp | 46 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 7c1e0daef..daa3154d7 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -105,7 +105,9 @@ inline double get_supportedness_score(const Facestats &fc) float cosphi = fc.normal.dot(DOWN); float phi = 1.f - std::acos(cosphi) / float(PI); - // Only consider faces that have slopes below 90 deg: + // Phi is raised by 1.0 to not be less than zero when squared in the next + // step. If phi is greater than 0.5 (slope is > 90 deg) make phi zero + // to not skip this face in the overall score. phi = (1.f + phi) * (phi >= 0.5f); // Make the huge slopes more significant than the smaller slopes @@ -285,6 +287,26 @@ std::array find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn) } // namespace +// Assemble the mesh with the correct transformation to be used in rotation +// optimization. +TriangleMesh get_mesh_to_rotate(const ModelObject &mo) +{ + TriangleMesh mesh = mo.raw_mesh(); + mesh.require_shared_vertices(); + + ModelInstance *mi = mo.instances[0]; + auto rotation = Vec3d::Zero(); + auto offset = Vec3d::Zero(); + Transform3d trafo_instance = Geometry::assemble_transform(offset, + rotation, + mi->get_scaling_factor(), + mi->get_mirror()); + + mesh.transform(trafo_instance); + + return mesh; +} + Vec2d find_best_misalignment_rotation(const ModelObject & mo, const RotOptimizeParams ¶ms) { @@ -295,8 +317,7 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo, // We will use only one instance of this converted mesh to examine different // rotations - TriangleMesh mesh = mo.raw_mesh(); - mesh.require_shared_vertices(); + TriangleMesh mesh = get_mesh_to_rotate(mo); // To keep track of the number of iterations int status = 0; @@ -352,17 +373,7 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, // We will use only one instance of this converted mesh to examine different // rotations - TriangleMesh mesh = mo.raw_mesh(); - mesh.require_shared_vertices(); - - ModelInstance* mi = mo.instances[0]; - Vec3d rotation = mi->get_rotation(); - Transform3d trafo_instance = Geometry::assemble_transform(mi->get_offset().z() * Vec3d::UnitZ(), - rotation, - mi->get_scaling_factor(), - mi->get_mirror()); - - mesh.transform(trafo_instance); + TriangleMesh mesh = get_mesh_to_rotate(mo); // To keep track of the number of iterations unsigned status = 0; @@ -430,13 +441,6 @@ Vec2d find_least_supports_rotation(const ModelObject & mo, // Save the result rot = result.optimum; - std::cout << "Score was: " << result.score << std::endl; - -//auto rt = mo.instances[0]->get_rotation(); -//double score = get_supportedness_score(mesh, to_transform3f({rt(0), rt(1)})); -// std::cout << "Score was: " << score << std::endl; -// rot[0] = rt(0); -// rot[1] = rt(1); } return {rot[0], rot[1]}; From 900814ff475a4c1b1c2411eb7ef13e5704012286 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Aug 2021 16:03:20 +0200 Subject: [PATCH 3/4] Add tooltips for combo items in rotation optimization dialog --- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 28 +++++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 4 ---- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 27 ++++++++++-------------- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index a495db4f1..417a6a644 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -497,9 +497,6 @@ void GLGizmoRotate3D::on_render() m_gizmos[Z].render(); } -const char * GLGizmoRotate3D::RotoptimzeWindow::options[RotoptimizeJob::get_methods_count()]; -bool GLGizmoRotate3D::RotoptimzeWindow::options_valid = false; - GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, State & state, const Alignment &alignment) @@ -517,19 +514,24 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, ImGui::PushItemWidth(200.f); - size_t methods_cnt = RotoptimizeJob::get_methods_count(); - if (!options_valid) { - for (size_t i = 0; i < methods_cnt; ++i) - options[i] = RotoptimizeJob::get_method_names()[i].c_str(); + if (ImGui::BeginCombo(_L("Choose goal").c_str(), RotoptimizeJob::get_method_name(state.method_id).c_str())) { + for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) { + if (ImGui::Selectable(RotoptimizeJob::get_method_name(i).c_str())) { + state.method_id = i; + wxGetApp().app_config->set("sla_auto_rotate", + "method_id", + std::to_string(state.method_id)); + } - options_valid = true; + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(i).c_str()); + } + + ImGui::EndCombo(); } - int citem = state.method_id; - if (ImGui::Combo(_L("Choose goal").c_str(), &citem, options, methods_cnt) ) { - state.method_id = citem; - wxGetApp().app_config->set("sla_auto_rotate", "method_id", std::to_string(state.method_id)); - } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(state.method_id).c_str()); ImGui::Separator(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index a51f900bf..3245c4dbe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -138,10 +138,6 @@ private: class RotoptimzeWindow { ImGuiWrapper *m_imgui = nullptr; - - static const char * options []; - static bool options_valid; - public: struct State { diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index 3144f3c3e..3f10df044 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -15,13 +15,13 @@ class RotoptimizeJob : public PlaterJob using FindFn = std::function; - struct FindMethod { std::string name; FindFn findfn; }; + struct FindMethod { std::string name; FindFn findfn; std::string descr; }; static inline const FindMethod Methods[] = { - { L("Best surface quality"), sla::find_best_misalignment_rotation }, - { L("Least supports"), sla::find_least_supports_rotation }, + { L("Best surface quality"), sla::find_best_misalignment_rotation, L("Optimize object rotation for best surface quality.") }, + { L("Least supports"), sla::find_least_supports_rotation, L("Optimize object rotation to have minimum amount of overhangs needing support structures.") }, // Just a min area bounding box that is done for all methods anyway. - { L("Z axis only"), nullptr } + { L("Z axis only"), nullptr, L("Rotate the object only in Z axis to have the smallest bounding box.") } }; size_t m_method_id = 0; @@ -52,20 +52,15 @@ public: void finalize() override; static constexpr size_t get_methods_count() { return std::size(Methods); } - static const auto & get_method_names() + + static std::string get_method_name(size_t i) { - static bool m_method_names_valid = false; - static std::array m_method_names; + return _utf8(Methods[i].name); + } - if (!m_method_names_valid) { - - for (size_t i = 0; i < std::size(Methods); ++i) - m_method_names[i] = _utf8(Methods[i].name); - - m_method_names_valid = true; - } - - return m_method_names; + static std::string get_method_description(size_t i) + { + return _utf8(Methods[i].descr); } }; From c37bb0cdd1bf463c0afab4d6d4c889d743c26544 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Aug 2021 16:42:56 +0200 Subject: [PATCH 4/4] Extend tooltip texts --- src/slic3r/GUI/Jobs/RotoptimizeJob.hpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp index 3f10df044..bb4310e63 100644 --- a/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp +++ b/src/slic3r/GUI/Jobs/RotoptimizeJob.hpp @@ -17,12 +17,19 @@ class RotoptimizeJob : public PlaterJob struct FindMethod { std::string name; FindFn findfn; std::string descr; }; - static inline const FindMethod Methods[] = { - { L("Best surface quality"), sla::find_best_misalignment_rotation, L("Optimize object rotation for best surface quality.") }, - { L("Least supports"), sla::find_least_supports_rotation, L("Optimize object rotation to have minimum amount of overhangs needing support structures.") }, - // Just a min area bounding box that is done for all methods anyway. - { L("Z axis only"), nullptr, L("Rotate the object only in Z axis to have the smallest bounding box.") } - }; + static inline const FindMethod Methods[] + = {{L("Best surface quality"), + sla::find_best_misalignment_rotation, + L("Optimize object rotation for best surface quality.")}, + {L("Least supports"), + sla::find_least_supports_rotation, + L("Optimize object rotation to have minimum amount of overhangs needing support " + "structures.\nNote that this method will try to find the best surface of the object " + "for touching the print bed if no elevation is set.")}, + // Just a min area bounding box that is done for all methods anyway. + {L("Z axis only"), + nullptr, + L("Rotate the object only in Z axis to have the smallest bounding box.")}}; size_t m_method_id = 0; float m_accuracy = 0.75;