From f6445eefe3b2bb429fdb569267935c53f438290c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 10 Mar 2023 11:49:53 +0100 Subject: [PATCH 1/9] Fix a memory leak in placeholder parser --- src/libslic3r/PlaceholderParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 2dd5e8f8e..5235fd72e 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -234,6 +234,7 @@ namespace client delete m_data.s; m_type = TYPE_EMPTY; } + ~expr() { reset(); } enum Type { TYPE_EMPTY = 0, From fd890888280abf1ad7a1d4e1ee12d32157fadede Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 10 Mar 2023 13:08:45 +0100 Subject: [PATCH 2/9] Fix for SPE-1494 - When inches is enabled, then entered negative or zero value was transformed to the wrong one --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index c16d77751..2a172b91f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -1395,8 +1395,9 @@ void ObjectManipulation::on_change(const std::string& opt_key, int axis, double if (new_value > 0.0) change_size_value(axis, new_value); else { - new_value = m_cache.size(axis); - m_cache.size(axis) = 0.0; + Vec3d& size = m_imperial_units ? m_cache.size_inches : m_cache.size; + new_value = size(axis); + size(axis) = 0.0; m_cache.size_rounded(axis) = DBL_MAX; change_size_value(axis, new_value); } From 035997a0495ce7e7149a6f01a00faae4aed30817 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 10 Mar 2023 13:47:50 +0100 Subject: [PATCH 3/9] Deleted workaround related to UA localization --- src/slic3r/GUI/GUI_App.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0c4953e3a..076bef54b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2305,18 +2305,6 @@ bool GUI_App::load_language(wxString language, bool initial) // Override language at the active wxTranslations class (which is stored in the active m_wxLocale) // to load possibly different dictionary, for example, load Czech dictionary for Slovak language. wxTranslations::Get()->SetLanguage(language_dict); - { - // ysFIXME after fix for wxWidgets issue (https://github.com/wxWidgets/wxWidgets/issues/23210) - // UKR Localization specific workaround till the wxWidgets doesn't fixed: - // From wxWidgets 3.1.6 calls setlocation(0, wxInfoLanguage->LocaleTag), see (https://github.com/prusa3d/wxWidgets/commit/deef116a09748796711d1e3509965ee208dcdf0b#diff-7de25e9a71c4dce61bbf76492c589623d5b93fd1bb105ceaf0662075d15f4472), - // where LocaleTag is a Tag of locale in BCP 47 - like notation. - // For Ukrainian Language LocaleTag is "uk". - // But setlocale(0, "uk") returns "English_United Kingdom.1252" instead of "uk", - // and, as a result, locales are set to English_United Kingdom - - if (language_info->CanonicalName == "uk") - setlocale(0, language_info->GetCanonicalWithRegion().data()); - } m_wxLocale->AddCatalog(SLIC3R_APP_KEY); m_imgui->set_language(into_u8(language_info->CanonicalName)); //FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only. From c2fe61261dd4490b63114c8154575e036617c9ec Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 10 Mar 2023 15:21:20 +0100 Subject: [PATCH 4/9] Export STL - check path extension in lower case. #10000 --- src/slic3r/GUI/Plater.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 905849b8a..22ce86c9f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6503,9 +6503,9 @@ void Plater::export_stl_obj(bool extended, bool selection_only) } } - if (path.EndsWith(".stl")) + if (path.Lower().EndsWith(".stl")) Slic3r::store_stl(path_u8.c_str(), &mesh, true); - else if (path.EndsWith(".obj")) + else if (path.Lower().EndsWith(".obj")) Slic3r::store_obj(path_u8.c_str(), &mesh); // p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path)); } From 2f9c98311498e7f312157bbf0090447019609fd1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 6 Mar 2023 16:56:29 +0100 Subject: [PATCH 5/9] Fix of wrong use of GUI::format --- src/slic3r/GUI/FileArchiveDialog.cpp | 6 +++--- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/FileArchiveDialog.cpp b/src/slic3r/GUI/FileArchiveDialog.cpp index 7337258cb..fc2e27bf8 100644 --- a/src/slic3r/GUI/FileArchiveDialog.cpp +++ b/src/slic3r/GUI/FileArchiveDialog.cpp @@ -206,7 +206,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar reduce_stack(stack, struct_size); } if (!file.has_extension() && stack.size() == struct_size) - stack.push_back(avc->get_model()->AddFile((stack.empty() ? std::shared_ptr(nullptr) : stack.back()), GUI::format_wxstr(file.filename().string()), true)); // filename string to wstring? + stack.push_back(avc->get_model()->AddFile((stack.empty() ? std::shared_ptr(nullptr) : stack.back()), boost::nowide::widen(file.filename().string()), true)); // filename string to wstring? return struct_size + 1; }; @@ -223,7 +223,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar path = boost::filesystem::path(extra.substr(0, extra_size)); } else { wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = GUI::format(wname); + std::string name = boost::nowide::narrow(wname); path = boost::filesystem::path(name); } assert(!path.empty()); @@ -247,7 +247,7 @@ FileArchiveDialog::FileArchiveDialog(wxWindow* parent_window, mz_zip_archive* ar if (!stack.empty()) 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? + m_avc->get_model()->AddFile(parent, boost::nowide::widen(path.filename().string()), false)->set_fullpath(/*std::move(path)*/path); // filename string to wstring? entry_count++; } } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22ce86c9f..defc8be3f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5576,7 +5576,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path) for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(&archive, i, &stat)) { wxString wname = boost::nowide::widen(stat.m_filename); - std::string name = GUI::format(wname); + std::string name = boost::nowide::narrow(wname); fs::path archive_path(name); std::string extra(1024, 0); From 62b84b71128409737ba59d1585939b1974a12782 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Mar 2023 10:34:00 +0100 Subject: [PATCH 6/9] Fix arrange contour cache not addressing last segment - Only when shape closure type was OPEN, which it is for ExPolygon - Also remove duplicated first corner --- .../include/libnest2d/geometry_traits.hpp | 2 +- .../include/libnest2d/placers/nfpplacer.hpp | 33 ++++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/libnest2d/include/libnest2d/geometry_traits.hpp b/src/libnest2d/include/libnest2d/geometry_traits.hpp index 134ec73a0..a179e4c94 100644 --- a/src/libnest2d/include/libnest2d/geometry_traits.hpp +++ b/src/libnest2d/include/libnest2d/geometry_traits.hpp @@ -871,7 +871,7 @@ template auto rcend(const P& p) -> decltype(_backward(cbegin(p))) template TPoint

front(const P& p) { return *shapelike::cbegin(p); } template TPoint

back (const P& p) { - return *backward(shapelike::cend(p)); + return *std::prev(shapelike::cend(p)); } // Optional, does nothing by default diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 5b5311d90..a17d54982 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -157,26 +157,34 @@ template class EdgeCache { void createCache(const RawShape& sh) { { // For the contour - auto first = shapelike::cbegin(sh); - auto next = std::next(first); - auto endit = shapelike::cend(sh); + auto first = sl::cbegin(sh); + auto endit = sl::cend(sh); + auto next = first == endit ? endit : std::next(first); - contour_.distances.reserve(shapelike::contourVertexCount(sh)); + contour_.distances.reserve(sl::contourVertexCount(sh)); while(next != endit) { contour_.emap.emplace_back(*(first++), *(next++)); contour_.full_distance += length(contour_.emap.back()); contour_.distances.emplace_back(contour_.full_distance); } + + if constexpr (ClosureTypeV == Closure::OPEN) { + if (sl::contourVertexCount(sh) > 0) { + contour_.emap.emplace_back(sl::back(sh), sl::front(sh)); + contour_.full_distance += length(contour_.emap.back()); + contour_.distances.emplace_back(contour_.full_distance); + } + } } for(auto& h : shapelike::holes(sh)) { // For the holes - auto first = h.begin(); - auto next = std::next(first); - auto endit = h.end(); + auto first = sl::cbegin(h); + auto endit = sl::cend(h); + auto next = first == endit ? endit :std::next(first); ContourCache hc; - hc.distances.reserve(endit - first); + hc.distances.reserve(sl::contourVertexCount(h)); while(next != endit) { hc.emap.emplace_back(*(first++), *(next++)); @@ -184,6 +192,14 @@ template class EdgeCache { hc.distances.emplace_back(hc.full_distance); } + if constexpr (ClosureTypeV == Closure::OPEN) { + if (sl::contourVertexCount(h) > 0) { + hc.emap.emplace_back(sl::back(sh), sl::front(sh)); + hc.full_distance += length(hc.emap.back()); + hc.distances.emplace_back(hc.full_distance); + } + } + holes_.emplace_back(std::move(hc)); } } @@ -206,7 +222,6 @@ template class EdgeCache { contour_.corners.reserve(N / S + 1); contour_.corners.emplace_back(0.0); auto N_1 = N-1; - contour_.corners.emplace_back(0.0); for(size_t i = 0; i < N_1; i += S) { contour_.corners.emplace_back( contour_.distances.at(i) / contour_.full_distance); From eb31dcec4488a3137ee217a73e4438b4138ccbd4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 13 Mar 2023 11:13:33 +0100 Subject: [PATCH 7/9] Fixed crash when deleting 2nd instance of object --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 2a172b91f..415f008fb 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -707,6 +707,12 @@ void ObjectManipulation::update_ui_from_settings() void ObjectManipulation::update_settings_value(const Selection& selection) { + if (selection.is_empty()) { + // No selection, reset the cache. + reset_settings_value(); + return; + } + m_new_move_label_string = L("Position"); m_new_rotate_label_string = L("Rotation"); m_new_scale_label_string = L("Scale factors"); @@ -836,11 +842,6 @@ void ObjectManipulation::update_settings_value(const Selection& selection) #endif // ENABLE_WORLD_COORDINATE m_new_enabled = true; } - else { - // No selection, reset the cache. -// assert(selection.is_empty()); - reset_settings_value(); - } } void ObjectManipulation::update_if_dirty() From 1154f51aabcf581bcd6a7123f699ee1924973e2a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Mar 2023 13:52:21 +0100 Subject: [PATCH 8/9] Fix failing libnest2d tests Fix the test itself --- tests/libnest2d/libnest2d_tests_main.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp index 97c7ef99d..f6df83b77 100644 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ b/tests/libnest2d/libnest2d_tests_main.cpp @@ -1024,9 +1024,15 @@ TEST_CASE("pointOnPolygonContour", "[Geometry]") { REQUIRE(getX(first) == getX(ecache.coords(0))); REQUIRE(getY(first) == getY(ecache.coords(0))); - auto last = *std::prev(input.end()); - REQUIRE(getX(last) == getX(ecache.coords(1.0))); - REQUIRE(getY(last) == getY(ecache.coords(1.0))); + if constexpr (ClosureTypeV == Closure::CLOSED) { + auto last = *std::prev(input.end()); + REQUIRE(getX(last) == getX(ecache.coords(1.0))); + REQUIRE(getY(last) == getY(ecache.coords(1.0))); + } else { + auto last = *input.begin(); + REQUIRE(getX(last) == getX(ecache.coords(1.0))); + REQUIRE(getY(last) == getY(ecache.coords(1.0))); + } for(int i = 0; i <= 100; i++) { auto v = ecache.coords(i*(0.01)); From e99ee946afbaf6e6db9413ccacdd03233c4743d1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 14 Mar 2023 08:21:38 +0100 Subject: [PATCH 9/9] Avoid updating and sending to gpu sequential print clearance contours at every frame. Cache them instead and update only their transforms. --- src/libslic3r/Print.cpp | 4 +- src/libslic3r/Print.hpp | 6 +- src/slic3r/GUI/GLCanvas3D.cpp | 165 +++++++++++++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 32 ++++- src/slic3r/GUI/GLModel.cpp | 32 +++++ src/slic3r/GUI/GLModel.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 + src/slic3r/GUI/Plater.cpp | 10 +- 8 files changed, 200 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d2b1da4b5..1eb1b41a4 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -466,13 +466,13 @@ std::string Print::validate(std::string* warning) const return L("The supplied settings will cause an empty print."); if (m_config.complete_objects) { - if (!sequential_print_horizontal_clearance_valid(*this, const_cast(&m_sequential_print_clearance_polygons))) + if (!sequential_print_horizontal_clearance_valid(*this, const_cast(&m_sequential_print_clearance_contours))) return L("Some objects are too close; your extruder will collide with them."); if (!sequential_print_vertical_clearance_valid(*this)) return L("Some objects are too tall and cannot be printed without extruder collisions."); } else - const_cast(&m_sequential_print_clearance_polygons)->clear(); + const_cast(&m_sequential_print_clearance_contours)->clear(); if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) { return L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together."); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 580980cfc..be0a6d516 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -597,7 +597,7 @@ public: const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } - const Polygons& get_sequential_print_clearance_polygons() const { return m_sequential_print_clearance_polygons; } + const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; } static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); protected: @@ -647,8 +647,8 @@ private: // Estimated print time, filament consumed. PrintStatistics m_print_statistics; - // Cache to store sequential print clearance polygons - Polygons m_sequential_print_clearance_polygons; + // Cache to store sequential print clearance contours + Polygons m_sequential_print_clearance_contours; // To allow GCode to set the Print's GCodeExport step status. friend class GCode; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e137a1b3e..6a089a746 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -880,20 +880,22 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas ImGui::PopStyleVar(2); } -void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons) +void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours) { - m_perimeter.reset(); + m_contours.clear(); + m_instances.clear(); m_fill.reset(); - if (polygons.empty()) + + if (contours.empty()) return; if (m_render_fill) { GLModel::Geometry fill_data; fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 }; - fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; + fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; // vertices + indices - const ExPolygons polygons_union = union_ex(polygons); + const ExPolygons polygons_union = union_ex(contours.contours); unsigned int vertices_counter = 0; for (const ExPolygon& poly : polygons_union) { const std::vector triangulation = triangulate_expolygon_3d(poly); @@ -906,17 +908,42 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } } - m_fill.init_from(std::move(fill_data)); } - m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting + for (size_t i = 0; i < contours.contours.size(); ++i) { + GLModel& model = m_contours.emplace_back(GLModel()); + model.init_from(contours.contours[i], 0.025f); // add a small positive z to avoid z-fighting + } + + if (contours.trafos.has_value()) { + // create the requested instances + for (const auto& instance : contours.trafos.value()) { + m_instances.emplace_back(instance.first, instance.second); + } + } + else { + // no instances have been specified + // create one instance for every polygon + for (size_t i = 0; i < contours.contours.size(); ++i) { + m_instances.emplace_back(i, Transform3f::Identity()); + } + } +} + +void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector& trafos) +{ + assert(trafos.size() == m_instances.size()); + for (size_t i = 0; i < trafos.size(); ++i) { + m_instances[i].second = trafos[i]; + } } void GLCanvas3D::SequentialPrintClearance::render() { - const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; - const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; + const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; + const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; + const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f }; GLShaderProgram* shader = wxGetApp().get_shader("flat"); if (shader == nullptr) @@ -933,9 +960,34 @@ void GLCanvas3D::SequentialPrintClearance::render() glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - m_perimeter.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR); - m_perimeter.render(); - m_fill.render(); + if (m_render_fill) + m_fill.render(); + +#if ENABLE_GL_CORE_PROFILE + if (OpenGLManager::get_gl_info().is_core_profile()) { + shader->stop_using(); + + shader = wxGetApp().get_shader("dashed_thick_lines"); + if (shader == nullptr) + return; + + shader->start_using(); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + const std::array& viewport = camera.get_viewport(); + shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3]))); + shader->set_uniform("width", 1.0f); + shader->set_uniform("gap_size", 0.0f); + } + else +#endif // ENABLE_GL_CORE_PROFILE + glsafe(::glLineWidth(2.0f)); + + for (const auto& [id, trafo] : m_instances) { + shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo); + assert(id < m_contours.size()); + m_contours[id].set_color(m_render_fill ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR); + m_contours[id].render(); + } glsafe(::glDisable(GL_BLEND)); glsafe(::glEnable(GL_CULL_FACE)); @@ -3531,7 +3583,11 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) if (wipe_tower_origin != Vec3d::Zero()) post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); - reset_sequential_print_clearance(); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + m_sequential_print_clearance_first_displacement = true; + update_sequential_clearance(); + m_sequential_print_clearance.set_evaluating(true); + } m_dirty = true; } @@ -3609,6 +3665,12 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type) if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + m_sequential_print_clearance_first_displacement = true; + update_sequential_clearance(); + m_sequential_print_clearance.set_evaluating(true); + } + m_dirty = true; } @@ -3674,6 +3736,12 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type) if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED)); + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + m_sequential_print_clearance_first_displacement = true; + update_sequential_clearance(); + m_sequential_print_clearance.set_evaluating(true); + } + m_dirty = true; } @@ -3942,7 +4010,7 @@ void GLCanvas3D::update_sequential_clearance() return; // collects instance transformations from volumes - // first define temporary cache + // first: define temporary cache unsigned int instances_count = 0; std::vector>> instance_transforms; for (size_t obj = 0; obj < m_model->objects.size(); ++obj) { @@ -3957,7 +4025,7 @@ void GLCanvas3D::update_sequential_clearance() if (instances_count == 1) return; - // second fill temporary cache with data from volumes + // second: fill temporary cache with data from volumes for (const GLVolume* v : m_volumes.volumes) { if (v->is_modifier || v->is_wipe_tower) continue; @@ -3967,14 +4035,24 @@ void GLCanvas3D::update_sequential_clearance() transform = v->get_instance_transformation(); } + // helper function to calculate the transformation to be applied to the sequential print clearance contours + auto instance_trafo = [](const Transform3d& hull_trafo, const Geometry::Transformation& inst_trafo) { + Vec3d offset = inst_trafo.get_offset() - hull_trafo.translation(); + offset.z() = 0.0; + return Geometry::translation_transform(offset) * + Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ()); + }; + + set_sequential_print_clearance_render_fill(false); + // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) // this is done only the first time this method is called while moving the mouse, // the results are then cached for following displacements if (m_sequential_print_clearance_first_displacement) { - m_sequential_print_clearance.m_hull_2d_cache.clear(); + m_sequential_print_clearance.m_hulls_2d_cache.clear(); 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()); + m_sequential_print_clearance.m_hulls_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]; ModelInstance* model_instance0 = model_object->instances.front(); @@ -3986,38 +4064,51 @@ void GLCanvas3D::update_sequential_clearance() shrink_factor, jtRound, mitter_limit).front(); - Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); - cache_hull_2d.reserve(hull_2d.points.size()); + Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first; + new_hull_2d.reserve(hull_2d.points.size()); const Transform3d inv_trafo = trafo.get_matrix().inverse(); for (const Point& p : hull_2d.points) { - cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale(p.x()), unscale(p.y()), 0.0)); + new_hull_2d.emplace_back(Vec3d(unscale(p.x()), unscale(p.y()), 0.0)); } } + + ContoursList contours; + contours.contours.reserve(instance_transforms.size()); + contours.trafos = std::vector>(); + contours.trafos.value().reserve(instances_count); + for (size_t i = 0; i < instance_transforms.size(); ++i) { + const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; + Points hull_pts; + hull_pts.reserve(hull.size()); + for (size_t j = 0; j < hull.size(); ++j) { + hull_pts.emplace_back(scaled(hull[j].x()), scaled(hull[j].y())); + } + contours.contours.emplace_back(Geometry::convex_hull(std::move(hull_pts))); + + const auto& instances = instance_transforms[i]; + for (const auto& instance : instances) { + contours.trafos.value().emplace_back(i, instance_trafo(hull_trafo, instance.value())); + } + } + + set_sequential_print_clearance_contours(contours); m_sequential_print_clearance_first_displacement = false; } - - // calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) - Polygons polygons; - polygons.reserve(instances_count); - for (size_t i = 0; i < instance_transforms.size(); ++i) { - const auto& instances = instance_transforms[i]; - for (const auto& instance : instances) { - 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()); - for (size_t j = 0; j < hull_2d.size(); ++j) { - const Vec3d p = trafo * hull_2d[j]; - inst_pts.emplace_back(scaled(p.x()), scaled(p.y())); + else { + std::vector trafos; + trafos.reserve(instances_count); + for (size_t i = 0; i < instance_transforms.size(); ++i) { + const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i]; + const auto& instances = instance_transforms[i]; + for (const auto& instance : instances) { + trafos.emplace_back(instance_trafo(hull_trafo, instance.value())); } - polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts))); } + m_sequential_print_clearance.update_instances_trafos(trafos); } // sends instances 2d hulls to be rendered set_sequential_print_clearance_visible(true); - set_sequential_print_clearance_render_fill(false); - set_sequential_print_clearance_polygons(polygons); } bool GLCanvas3D::is_object_sinking(int object_idx) const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index b5914e456..86a3cc71b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -587,22 +587,38 @@ public: return ret; } + struct ContoursList + { + // list of unique contours + std::vector contours; + // if defined: list of transforms to apply to contours + std::optional>> trafos; + + bool empty() const { return contours.empty(); } + }; + private: void load_arrange_settings(); class SequentialPrintClearance { GLModel m_fill; - GLModel m_perimeter; + // list of unique contours + std::vector m_contours; + // list of transforms used to render the contours + std::vector> m_instances; bool m_render_fill{ true }; bool m_visible{ false }; + bool m_evaluating{ false }; - std::vector m_hull_2d_cache; + std::vector> m_hulls_2d_cache; public: - void set_polygons(const Polygons& polygons); + void set_contours(const ContoursList& contours); + void update_instances_trafos(const std::vector& trafos); void set_render_fill(bool render_fill) { m_render_fill = render_fill; } void set_visible(bool visible) { m_visible = visible; } + void set_evaluating(bool evaluating) { m_evaluating = evaluating; } void render(); friend class GLCanvas3D; @@ -927,7 +943,7 @@ public: void reset_sequential_print_clearance() { m_sequential_print_clearance.set_visible(false); m_sequential_print_clearance.set_render_fill(false); - m_sequential_print_clearance.set_polygons(Polygons()); + m_sequential_print_clearance.set_contours(ContoursList()); } void set_sequential_print_clearance_visible(bool visible) { @@ -938,8 +954,12 @@ public: m_sequential_print_clearance.set_render_fill(render_fill); } - void set_sequential_print_clearance_polygons(const Polygons& polygons) { - m_sequential_print_clearance.set_polygons(polygons); + void set_sequential_print_clearance_contours(const ContoursList& contours) { + m_sequential_print_clearance.set_contours(contours); + } + + void set_sequential_print_clearance_evaluating(bool evaluating) { + m_sequential_print_clearance.set_evaluating(evaluating); } void update_sequential_clearance(); diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 61736f9ac..3a7c00285 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -596,6 +596,38 @@ void GLModel::init_from(const indexed_triangle_set& its) } } +void GLModel::init_from(const Polygon& polygon, float z) +{ + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); + return; + } + + Geometry& data = m_render_data.geometry; + data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 }; + + const size_t segments_count = polygon.points.size(); + data.reserve_vertices(2 * segments_count); + data.reserve_indices(2 * segments_count); + + // vertices + indices + unsigned int vertices_counter = 0; + for (size_t i = 0; i < segments_count; ++i) { + const Point& p0 = polygon.points[i]; + const Point& p1 = (i == segments_count - 1) ? polygon.points.front() : polygon.points[i + 1]; + data.add_vertex(Vec3f(unscale(p0.x()), unscale(p0.y()), z)); + data.add_vertex(Vec3f(unscale(p1.x()), unscale(p1.y()), z)); + vertices_counter += 2; + data.add_line(vertices_counter - 2, vertices_counter - 1); + } + + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + m_bounding_box.merge(data.extract_position_3(i).cast()); + } +} + void GLModel::init_from(const Polygons& polygons, float z) { if (is_initialized()) { diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index ef4ab6d47..b93f6fad5 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -227,6 +227,7 @@ namespace GUI { void init_from(const TriangleMesh& mesh); #endif // ENABLE_SMOOTH_NORMALS void init_from(const indexed_triangle_set& its); + void init_from(const Polygon& polygon, float z); void init_from(const Polygons& polygons, float z); bool init_from_file(const std::string& filename); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index af93c07df..449ebf945 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -971,6 +971,8 @@ bool GLGizmosManager::activate_gizmo(EType type) new_gizmo.register_raycasters_for_picking(); + m_parent.reset_sequential_print_clearance(); + // sucessful activation of gizmo return true; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a655357fd..9cc28acfd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3293,10 +3293,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; if (printer_technology == ptFFF) { const Print* print = background_process.fff_print(); - const Polygons polygons = print->get_sequential_print_clearance_polygons(); - view3D->get_canvas3d()->set_sequential_print_clearance_visible(!polygons.empty()); - view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(!polygons.empty()); - view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons); + GLCanvas3D::ContoursList contours; + contours.contours = print->get_sequential_print_clearance_contours(); + view3D->get_canvas3d()->set_sequential_print_clearance_visible(!contours.empty()); + view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(!contours.empty()); + view3D->get_canvas3d()->set_sequential_print_clearance_contours(contours); + view3D->get_canvas3d()->set_sequential_print_clearance_evaluating(false); } } }