From 5a1809579ec60397a7ff60d64ea393f40b9bb4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Tue, 19 Oct 2021 12:54:27 +0200 Subject: [PATCH 01/56] Fixed unintended space after ImGui::SliderFloat in the hollow and sla supports gizmos. --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 49 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 8 ++-- src/slic3r/GUI/ImGuiWrapper.cpp | 18 ++++++- src/slic3r/GUI/ImGuiWrapper.hpp | 6 ++- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index f7feed44ae..d45a2e6137 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -508,20 +508,23 @@ RENDER_AGAIN: m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float settings_sliders_left = - std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, - m_imgui->calc_text_size(m_desc.at("quality")).x, - m_imgui->calc_text_size(m_desc.at("closing_distance")).x, - m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, - m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) - + m_imgui->scaled(1.f); + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, + m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(0.5f); + + const float settings_sliders_left = + std::max(std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, + m_imgui->calc_text_size(m_desc.at("quality")).x, + m_imgui->calc_text_size(m_desc.at("closing_distance")).x, + m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, + m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) + m_imgui->scaled(0.5f), clipping_slider_left); - const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); + const float button_preview_width = m_imgui->calc_button_size(m_desc.at("preview")).x; + float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); - window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x); + window_width = std::max(window_width, button_preview_width); if (m_imgui->button(m_desc["preview"])) hollow_mesh(); @@ -544,9 +547,9 @@ RENDER_AGAIN: float max_tooltip_width = ImGui::GetFontSize() * 20.0f; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("offset")); - ImGui::SameLine(settings_sliders_left); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - settings_sliders_left); - m_imgui->slider_float(" ", &offset, offset_min, offset_max, "%.1f mm"); + m_imgui->slider_float("##offset", &offset, offset_min, offset_max, "%.1f mm"); if (ImGui::IsItemHovered()) m_imgui->tooltip((_utf8(opts[0].second->tooltip)).c_str(), max_tooltip_width); @@ -557,8 +560,8 @@ RENDER_AGAIN: if (current_mode >= quality_mode) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("quality")); - ImGui::SameLine(settings_sliders_left); - m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f"); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); + m_imgui->slider_float("##quality", &quality, quality_min, quality_max, "%.1f"); if (ImGui::IsItemHovered()) m_imgui->tooltip((_utf8(opts[1].second->tooltip)).c_str(), max_tooltip_width); @@ -570,8 +573,8 @@ RENDER_AGAIN: if (current_mode >= closing_d_mode) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("closing_distance")); - ImGui::SameLine(settings_sliders_left); - m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); + m_imgui->slider_float("##closing_distance", &closing_d, closing_d_min, closing_d_max, "%.1f mm"); if (ImGui::IsItemHovered()) m_imgui->tooltip((_utf8(opts[2].second->tooltip)).c_str(), max_tooltip_width); @@ -614,11 +617,11 @@ RENDER_AGAIN: m_new_hole_radius = diameter_upper_cap / 2.f; ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("hole_diameter")); - ImGui::SameLine(diameter_slider_left); + ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); ImGui::PushItemWidth(window_width - diameter_slider_left); float diam = 2.f * m_new_hole_radius; - m_imgui->slider_float("", &diam, 1.f, 15.f, "%.1f mm", 1.f, false); + m_imgui->slider_float("##hole_diameter", &diam, 1.f, 15.f, "%.1f mm", 1.f, false); // Let's clamp the value (which could have been entered by keyboard) to a larger range // than the slider. This allows entering off-scale values and still protects against //complete non-sense. @@ -630,8 +633,8 @@ RENDER_AGAIN: ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc["hole_depth"]); - ImGui::SameLine(diameter_slider_left); - m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false); + ImGui::SameLine(diameter_slider_left, m_imgui->get_item_spacing().x); + m_imgui->slider_float("##hole_depth", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false); // Same as above: m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f); @@ -697,10 +700,10 @@ RENDER_AGAIN: } } - ImGui::SameLine(clipping_slider_left); - ImGui::PushItemWidth(window_width - clipping_slider_left); + ImGui::SameLine(settings_sliders_left, m_imgui->get_item_spacing().x); + ImGui::PushItemWidth(window_width - settings_sliders_left); float clp_dist = m_c->object_clipper()->get_position(); - if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) + if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position(clp_dist, true); // make sure supports are shown/hidden as appropriate @@ -732,7 +735,7 @@ RENDER_AGAIN: if (force_refresh) m_parent.set_as_dirty(); - + if (config_changed) m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b7a6d89fa3..ccc67b6305 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -673,7 +673,7 @@ RENDER_AGAIN: // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene // - take correct undo/redo snapshot after the user is done with moving the slider float initial_value = m_new_point_head_diameter; - m_imgui->slider_float("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); + m_imgui->slider_float("##head_diameter", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); if (ImGui::IsItemClicked()) { if (m_old_point_head_diameter == 0.f) m_old_point_head_diameter = initial_value; @@ -733,7 +733,7 @@ RENDER_AGAIN: float density = static_cast(opts[0])->value; float minimal_point_distance = static_cast(opts[1])->value; - m_imgui->slider_float("", &minimal_point_distance, 0.f, 20.f, "%.f mm"); + m_imgui->slider_float("##minimal_point_distance", &minimal_point_distance, 0.f, 20.f, "%.f mm"); bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider @@ -742,7 +742,7 @@ RENDER_AGAIN: m_imgui->text(m_desc.at("points_density")); ImGui::SameLine(settings_sliders_left); - m_imgui->slider_float(" ", &density, 0.f, 200.f, "%.f %%"); + m_imgui->slider_float("##points_density", &density, 0.f, 200.f, "%.f %%"); slider_clicked |= ImGui::IsItemClicked(); slider_edited |= ImGui::IsItemEdited(); slider_released |= ImGui::IsItemDeactivatedAfterEdit(); @@ -802,7 +802,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); float clp_dist = m_c->object_clipper()->get_position(); - if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f")) + if (m_imgui->slider_float("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f")) m_c->object_clipper()->set_position(clp_dist, true); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index fe6c18db9b..22dccc695a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -280,7 +280,7 @@ void ImGuiWrapper::render() m_new_frame_open = false; } -ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) +ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) const { auto text_utf8 = into_u8(text); ImVec2 size = ImGui::CalcTextSize(text_utf8.c_str(), NULL, false, wrap_width); @@ -293,6 +293,22 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text, float wrap_width) return size; } +ImVec2 ImGuiWrapper::calc_button_size(const wxString &text, const ImVec2 &button_size) const +{ + const ImVec2 text_size = this->calc_text_size(text); + const ImGuiContext &g = *GImGui; + const ImGuiStyle &style = g.Style; + + return ImGui::CalcItemSize(button_size, text_size.x + style.FramePadding.x * 2.0f, text_size.y + style.FramePadding.y * 2.0f); +} + +ImVec2 ImGuiWrapper::get_item_spacing() const +{ + const ImGuiContext &g = *GImGui; + const ImGuiStyle &style = g.Style; + return g.Style.ItemSpacing; +} + float ImGuiWrapper::get_slider_float_height() const { const ImGuiContext& g = *GImGui; diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 3712ff6a87..27cd098ff1 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -53,9 +53,11 @@ public: float scaled(float x) const { return x * m_font_size; } ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } - ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f); + ImVec2 calc_text_size(const wxString &text, float wrap_width = -1.0f) const; + ImVec2 calc_button_size(const wxString &text, const ImVec2 &button_size = ImVec2(0, 0)) const; - float get_slider_float_height() const; + ImVec2 get_item_spacing() const; + float get_slider_float_height() const; void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha); From 79dd007ec7c403e5f781ffc9e0d078d2fd535c57 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 19 Oct 2021 16:15:27 +0200 Subject: [PATCH 02/56] Fix bloating of history with live preview --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 38 +++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 34 ++++++++++++++++++-- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index c43f215d94..d61033ec49 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -294,6 +294,7 @@ void GLGizmoSimplify::live_preview() { // wait until cancel if (m_worker.joinable()) { m_state = State::canceling; + m_dealy_process_cv.notify_one(); m_worker.join(); } } @@ -304,20 +305,38 @@ void GLGizmoSimplify::live_preview() { void GLGizmoSimplify::process() { - class SimplifyCanceledException : public std::exception { - public: - const char* what() const throw() { return L("Model simplification has been canceled"); } - }; + if (m_volume == nullptr) return; + if (m_volume->mesh().its.indices.empty()) return; + size_t count_triangles = m_volume->mesh().its.indices.size(); + if (m_configuration.use_count && + m_configuration.wanted_count >= count_triangles) + return; - if (!m_original_its.has_value()) + // when not store original volume store it for cancelation + if (!m_original_its.has_value()) { m_original_its = m_volume->mesh().its; // copy - auto plater = wxGetApp().plater(); - plater->take_snapshot(_L("Simplify ") + m_volume->name); - plater->clear_before_change_mesh(m_obj_index); + // store previous state + auto plater = wxGetApp().plater(); + plater->take_snapshot(_L("Simplify ") + m_volume->name); + plater->clear_before_change_mesh(m_obj_index); + } + m_progress = 0; if (m_worker.joinable()) m_worker.join(); - m_worker = std::thread([this]() { + + m_worker = std::thread([this]() { + {// delay before process + std::unique_lock lk(m_state_mutex); + auto is_modify = [this]() { return m_state == State::canceling; }; + if (m_dealy_process_cv.wait_for(lk, m_gui_cfg->prcess_delay, is_modify)) { + // exist modification + m_state = State::settings; + request_rerender(); + return; + } + } + // store original triangles uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0; float max_error = (!m_configuration.use_count) ? m_configuration.max_error : std::numeric_limits::max(); @@ -357,6 +376,7 @@ void GLGizmoSimplify::process() } void GLGizmoSimplify::set_its(indexed_triangle_set &its) { + if (m_volume == nullptr) return; // could appear after process m_volume->set_mesh(its); m_volume->calculate_convex_hull(); m_volume->set_new_unique_id(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index b609c5cdd0..7935123446 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -7,6 +7,9 @@ #include "GLGizmoPainterBase.hpp" // for render wireframe #include "admesh/stl.h" // indexed_triangle_set #include +#include +#include +#include #include #include @@ -47,6 +50,7 @@ private: void set_its(indexed_triangle_set &its); void create_gui_cfg(); void request_rerender(); + // move to global functions static ModelVolume *get_volume(const Selection &selection, Model &model); static const ModelVolume *get_volume(const GLVolume::CompositeID &cid, const Model &model); @@ -62,10 +66,14 @@ private: size_t m_obj_index; std::optional m_original_its; - bool m_show_wireframe; + bool m_show_wireframe; volatile bool m_need_reload; // after simplify, glReload must be on main thread + std::thread m_worker; + // wait before process + std::mutex m_state_mutex; + std::condition_variable m_dealy_process_cv; enum class State { settings, @@ -87,8 +95,14 @@ private: void fix_count_by_ratio(size_t triangle_count) { - wanted_count = static_cast( - std::round(triangle_count * (100.f-decimate_ratio) / 100.f)); + if (decimate_ratio <= 0.f) { + wanted_count = static_cast(triangle_count); + return; + } else if (decimate_ratio >= 1.f) { + wanted_count = 0; + return; + } + wanted_count = static_cast(std::round(triangle_count * (100.f-decimate_ratio) / 100.f)); } } m_configuration; @@ -106,6 +120,10 @@ private: // trunc model name when longer size_t max_char_in_name = 30; + + // to prevent freezing when move in gui + // delay before process in [ms] + std::chrono::duration prcess_delay = std::chrono::milliseconds(250); }; std::optional m_gui_cfg; @@ -122,6 +140,16 @@ private: void free_gpu(); GLuint m_wireframe_VBO_id, m_wireframe_IBO_id; size_t m_wireframe_IBO_size; + + // cancel exception + class SimplifyCanceledException: public std::exception + { + public: + const char *what() const throw() + { + return L("Model simplification has been canceled"); + } + }; }; } // namespace GUI From ee7d5db31ca5575b974048ddb2a0774c85b1fdee Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 19 Oct 2021 16:51:03 +0200 Subject: [PATCH 03/56] Add live preview on open gizmo Add delay 250ms before process for live move with slider Add short cut for unmodified mesh in processing --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 20 +++++++++++++++++--- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 11 +++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index d61033ec49..1d44aaa25c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -91,7 +91,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_is_valid_result = false; m_exist_preview = false; init_wireframe(); - + live_preview(); if (change_window_position) { ImVec2 pos = ImGui::GetMousePos(); pos.x -= m_gui_cfg->window_offset_x; @@ -308,9 +308,23 @@ void GLGizmoSimplify::process() if (m_volume == nullptr) return; if (m_volume->mesh().its.indices.empty()) return; size_t count_triangles = m_volume->mesh().its.indices.size(); - if (m_configuration.use_count && - m_configuration.wanted_count >= count_triangles) + // Is neccessary simplification + if ((m_configuration.use_count && m_configuration.wanted_count >= count_triangles) || + (!m_configuration.use_count && m_configuration.max_error <= 0.f)) { + + // Exist different original volume? + if (m_original_its.has_value() && + m_original_its->indices.size() != count_triangles) { + indexed_triangle_set its = *m_original_its; // copy + set_its(its); + } + m_is_valid_result = true; + + // re-render bargraph + set_dirty(); + m_parent.schedule_extra_frame(0); return; + } // when not store original volume store it for cancelation if (!m_original_its.has_value()) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 7935123446..21d0933efe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -95,14 +95,13 @@ private: void fix_count_by_ratio(size_t triangle_count) { - if (decimate_ratio <= 0.f) { + if (decimate_ratio <= 0.f) wanted_count = static_cast(triangle_count); - return; - } else if (decimate_ratio >= 1.f) { + else if (decimate_ratio >= 100.f) wanted_count = 0; - return; - } - wanted_count = static_cast(std::round(triangle_count * (100.f-decimate_ratio) / 100.f)); + else + wanted_count = static_cast(std::round( + triangle_count * (100.f - decimate_ratio) / 100.f)); } } m_configuration; From 76c0c76f5feda10528f06063b9c99a6685a858c0 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Tue, 19 Oct 2021 18:53:42 +0200 Subject: [PATCH 04/56] open simplification on center when open from notification --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 70 ++++++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp | 13 ++++- src/slic3r/GUI/Plater.cpp | 54 ++--------------- 3 files changed, 84 insertions(+), 53 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 1d44aaa25c..f823afb6ff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -24,6 +24,7 @@ GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, , m_obj_index(0) , m_need_reload(false) , m_show_wireframe(false) + , m_move_to_center(false) // translation for GUI size , tr_mesh_name(_u8L("Mesh name")) , tr_triangles(_u8L("Triangles")) @@ -50,6 +51,61 @@ bool GLGizmoSimplify::on_esc_key_down() { return true; } +// while opening needs GLGizmoSimplify to set window position +void GLGizmoSimplify::add_simplify_suggestion_notification( + const std::vector &object_ids, + const ModelObjectPtrs & objects, + NotificationManager & manager) +{ + std::vector big_ids; + big_ids.reserve(object_ids.size()); + auto is_big_object = [&objects](size_t object_id) { + const uint32_t triangles_to_suggest_simplify = 1000000; + if (object_id >= objects.size()) return false; // out of object index + ModelVolumePtrs &volumes = objects[object_id]->volumes; + if (volumes.size() != 1) return false; // not only one volume + size_t triangle_count = volumes.front()->mesh().its.indices.size(); + if (triangle_count < triangles_to_suggest_simplify) + return false; // small volume + return true; + }; + std::copy_if(object_ids.begin(), object_ids.end(), + std::back_inserter(big_ids), is_big_object); + if (big_ids.empty()) return; + + for (size_t object_id : big_ids) { + std::string t = _u8L( + "Processing model '@object_name' with more than 1M triangles " + "could be slow. It is highly recommend to reduce " + "amount of triangles."); + t.replace(t.find("@object_name"), sizeof("@object_name") - 1, + objects[object_id]->name); + // std::stringstream text; + // text << t << "\n"; + std::string hypertext = _u8L("Simplify model"); + + std::function open_simplify = + [object_id](wxEvtHandler *) { + auto plater = wxGetApp().plater(); + if (object_id >= plater->model().objects.size()) return true; + + Selection &selection = plater->canvas3D()->get_selection(); + selection.clear(); + selection.add_object((unsigned int) object_id); + + auto &manager = plater->canvas3D()->get_gizmos_manager(); + bool close_notification = true; + if(!manager.open_gizmo(GLGizmosManager::Simplify)) + return close_notification; + GLGizmoSimplify* simplify = dynamic_cast(manager.get_current()); + if (simplify == nullptr) return close_notification; + simplify->set_center_position(); + }; + manager.push_simplify_suggestion_notification( + t, objects[object_id]->id(), hypertext, open_simplify); + } +} + std::string GLGizmoSimplify::on_get_name() const { return _u8L("Simplify"); @@ -92,7 +148,15 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi m_exist_preview = false; init_wireframe(); live_preview(); - if (change_window_position) { + + // set window position + if (m_move_to_center && change_window_position) { + m_move_to_center = false; + auto parent_size = m_parent.get_canvas_size(); + ImVec2 pos(parent_size.get_width() / 2 - m_gui_cfg->window_offset_x, + parent_size.get_height() / 2 - m_gui_cfg->window_offset_y); + ImGui::SetNextWindowPos(pos, ImGuiCond_Always); + }else if (change_window_position) { ImVec2 pos = ImGui::GetMousePos(); pos.x -= m_gui_cfg->window_offset_x; pos.y -= m_gui_cfg->window_offset_y; @@ -474,6 +538,10 @@ void GLGizmoSimplify::request_rerender() { }); } +void GLGizmoSimplify::set_center_position() { + m_move_to_center = true; +} + bool GLGizmoSimplify::exist_volume(ModelVolume *volume) { auto objs = wxGetApp().plater()->model().objects; for (const auto &obj : objs) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index 21d0933efe..b978e9356d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -15,12 +15,14 @@ #include // GLUint -namespace Slic3r { +// for simplify suggestion +class ModelObjectPtrs; // std::vector +namespace Slic3r { class ModelVolume; namespace GUI { - +class NotificationManager; // for simplify suggestion class GLGizmoSimplify: public GLGizmoBase, public GLGizmoTransparentRender // GLGizmoBase { @@ -28,6 +30,10 @@ public: GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); virtual ~GLGizmoSimplify(); bool on_esc_key_down(); + static void add_simplify_suggestion_notification( + const std::vector &object_ids, + const ModelObjectPtrs & objects, + NotificationManager & manager); protected: virtual std::string on_get_name() const override; virtual void on_render_input_window(float x, float y, float bottom_limit) override; @@ -51,6 +57,7 @@ private: void create_gui_cfg(); void request_rerender(); + void set_center_position(); // move to global functions static ModelVolume *get_volume(const Selection &selection, Model &model); static const ModelVolume *get_volume(const GLVolume::CompositeID &cid, const Model &model); @@ -61,6 +68,8 @@ private: std::atomic_bool m_is_valid_result; // differ what to do in apply std::atomic_bool m_exist_preview; // set when process end + bool m_move_to_center; // opening gizmo + volatile int m_progress; // percent of done work ModelVolume *m_volume; // keep pointer to actual working volume size_t m_obj_index; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 97e4af218c..9a24d91cc8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -89,6 +89,7 @@ #include "PresetComboBoxes.hpp" #include "MsgDialog.hpp" #include "ProjectDirtyStateManager.hpp" +#include "Gizmos/GLGizmoSimplify.hpp" // create suggestion notification #ifdef __APPLE__ #include "Gizmos/GLGizmosManager.hpp" @@ -1779,7 +1780,6 @@ struct Plater::priv #endif // ENABLE_RELOAD_FROM_DISK_REPLACE_FILE void replace_with_stl(); void reload_all_from_disk(); - void create_simplify_notification(const std::vector& obj_ids); void set_current_panel(wxPanel* panel); void on_select_preset(wxCommandEvent&); @@ -2564,8 +2564,9 @@ std::vector Plater::priv::load_files(const std::vector& input_ // this is required because the selected object changed and the flatten on face an sla support gizmos need to be updated accordingly view3D->get_canvas3d()->update_gizmos_on_off_state(); } - - create_simplify_notification(obj_idxs); + + GLGizmoSimplify::add_simplify_suggestion_notification( + obj_idxs, model.objects, *notification_manager); return obj_idxs; } @@ -3755,53 +3756,6 @@ void Plater::priv::reload_all_from_disk() } } -void Plater::priv::create_simplify_notification(const std::vector& obj_ids) { - const uint32_t triangles_to_suggest_simplify = 1000000; - - std::vector big_ids; - big_ids.reserve(obj_ids.size()); - std::copy_if(obj_ids.begin(), obj_ids.end(), std::back_inserter(big_ids), - [this, triangles_to_suggest_simplify](size_t object_id) { - if (object_id >= model.objects.size()) return false; // out of object index - ModelVolumePtrs& volumes = model.objects[object_id]->volumes; - if (volumes.size() != 1) return false; // not only one volume - size_t triangle_count = volumes.front()->mesh().its.indices.size(); - if (triangle_count < triangles_to_suggest_simplify) return false; // small volume - return true; - }); - - if (big_ids.empty()) return; - - for (size_t object_id : big_ids) { - std::string t = _u8L( - "Processing model '@object_name' with more than 1M triangles " - "could be slow. It is highly recommend to reduce " - "amount of triangles."); - t.replace(t.find("@object_name"), sizeof("@object_name") - 1, - model.objects[object_id]->name); - //std::stringstream text; - //text << t << "\n"; - std::string hypertext = _u8L("Simplify model"); - - std::function open_simplify = [object_id](wxEvtHandler *) { - auto plater = wxGetApp().plater(); - if (object_id >= plater->model().objects.size()) return true; - - Selection &selection = plater->canvas3D()->get_selection(); - selection.clear(); - selection.add_object((unsigned int) object_id); - - auto &manager = plater->canvas3D()->get_gizmos_manager(); - manager.open_gizmo(GLGizmosManager::EType::Simplify); - return true; - }; - notification_manager->push_simplify_suggestion_notification(t, - model.objects[object_id]->id(), - hypertext, - open_simplify); - } -} - void Plater::priv::set_current_panel(wxPanel* panel) { if (std::find(panels.begin(), panels.end(), panel) == panels.end()) From 51056adee8df6aae09912b9da42126c437cff3b1 Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Fri, 13 Aug 2021 11:04:02 -0700 Subject: [PATCH 05/56] build_win: Don't delete preferences on clean build --- build_win.bat | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/build_win.bat b/build_win.bat index 600d6d6e6f..e00f8dbd4d 100644 --- a/build_win.bat +++ b/build_win.bat @@ -159,7 +159,7 @@ REM Build deps :BUILD_DEPS SET EXIT_STATUS=3 SET PS_CURRENT_STEP=deps -IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%" +IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%" .vs cd deps\build || GOTO :END cmake.exe .. -DDESTDIR="%PS_DESTDIR%" || GOTO :END (echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE%" @@ -171,7 +171,7 @@ REM Build app :BUILD_APP SET EXIT_STATUS=4 SET PS_CURRENT_STEP=app -IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY build "%PS_CUSTOM_RUN_FILE%" +IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY build "%PS_CUSTOM_RUN_FILE%" .vs cd build || GOTO :END REM Make sure we have a custom batch file skeleton for the run stage set PS_CUSTOM_BAT=%PS_CUSTOM_RUN_FILE% @@ -262,8 +262,11 @@ REM Functions and stubs start here. :RESOLVE_DESTDIR_CACHE @REM Resolves all DESTDIR cache values and sets PS_STEPS_DEFAULT -@REM Note: This just sets global variableq, so it doesn't use setlocal. -SET PS_DEPS_PATH_FILE_FOR_CONFIG=%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME% +@REM Note: This just sets global variables, so it doesn't use setlocal. +SET PS_DEPS_PATH_FILE_FOR_CONFIG=%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME% +mkdir "%~dp0build\.vs\%PS_ARCH%\%PS_CONFIG%" > nul 2> nul +REM Copy a legacy file if we don't have one in the proper location. +echo f|xcopy /D "%~dp0build\%PS_ARCH%\%PS_CONFIG%\%PS_DEPS_PATH_FILE_NAME%" "%PS_DEPS_PATH_FILE_FOR_CONFIG%" CALL :CANONICALIZE_PATH PS_DEPS_PATH_FILE_FOR_CONFIG IF EXIST "%PS_DEPS_PATH_FILE_FOR_CONFIG%" ( FOR /F "tokens=* USEBACKQ" %%I IN ("%PS_DEPS_PATH_FILE_FOR_CONFIG%") DO ( From 70faa27247346a757f410ee550dedd74d13862ac Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Fri, 13 Aug 2021 11:05:32 -0700 Subject: [PATCH 06/56] build_win: Reduce msbuild verbosity --- build_win.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build_win.bat b/build_win.bat index e00f8dbd4d..893d8c3448 100644 --- a/build_win.bat +++ b/build_win.bat @@ -163,7 +163,7 @@ IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_ cd deps\build || GOTO :END cmake.exe .. -DDESTDIR="%PS_DESTDIR%" || GOTO :END (echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE%" -msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% || GOTO :END +msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% /v:quiet || GOTO :END cd ..\.. IF /I "%PS_STEPS:~0,4%" EQU "deps" GOTO :RUN_APP @@ -183,7 +183,7 @@ FOR /F "tokens=2 delims=," %%I in ( ) do SET PS_PROJECT_IS_OPEN=%%~I cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST% || GOTO :END REM Skip the build step if we're using the undocumented app-cmake to regenerate the full config from inside devenv -IF "%PS_STEPS%" NEQ "app-cmake" msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% || GOTO :END +IF "%PS_STEPS%" NEQ "app-cmake" msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% /v:quiet || GOTO :END (echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE_FOR_CONFIG%" REM Run app From d4be22df8a42d98d754c129b787573aec7d247cd Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Fri, 13 Aug 2021 11:22:35 -0700 Subject: [PATCH 07/56] build_win: Retry on cmake cache failure --- build_win.bat | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build_win.bat b/build_win.bat index 893d8c3448..34fea8d617 100644 --- a/build_win.bat +++ b/build_win.bat @@ -161,7 +161,10 @@ SET EXIT_STATUS=3 SET PS_CURRENT_STEP=deps IF "%PS_STEPS_DIRTY%" EQU "" CALL :MAKE_OR_CLEAN_DIRECTORY deps\build "%PS_DEPS_PATH_FILE_NAME%" .vs cd deps\build || GOTO :END -cmake.exe .. -DDESTDIR="%PS_DESTDIR%" || GOTO :END +cmake.exe .. -DDESTDIR="%PS_DESTDIR%" +IF %ERRORLEVEL% NEQ 0 IF "%PS_STEPS_DIRTY%" NEQ "" ( + (del CMakeCache.txt && cmake.exe .. -DDESTDIR="%PS_DESTDIR%") || GOTO :END +) ELSE GOTO :END (echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE%" msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% /v:quiet || GOTO :END cd ..\.. @@ -181,7 +184,10 @@ SET PS_PROJECT_IS_OPEN= FOR /F "tokens=2 delims=," %%I in ( 'tasklist /V /FI "IMAGENAME eq devenv.exe " /NH /FO CSV ^| find "%PS_SOLUTION_NAME%"' ) do SET PS_PROJECT_IS_OPEN=%%~I -cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST% || GOTO :END +cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST% +IF %ERRORLEVEL% NEQ 0 IF "%PS_STEPS_DIRTY%" NEQ "" ( + (del CMakeCache.txt && cmake.exe .. -DCMAKE_PREFIX_PATH="%PS_DESTDIR%\usr\local" -DCMAKE_CONFIGURATION_TYPES=%PS_CONFIG_LIST%) || GOTO :END +) ELSE GOTO :END REM Skip the build step if we're using the undocumented app-cmake to regenerate the full config from inside devenv IF "%PS_STEPS%" NEQ "app-cmake" msbuild /m ALL_BUILD.vcxproj /p:Configuration=%PS_CONFIG% /v:quiet || GOTO :END (echo %PS_DESTDIR%)> "%PS_DEPS_PATH_FILE_FOR_CONFIG%" From 26cfc9ebb35894b69a664982e51c1daf07425044 Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Wed, 1 Sep 2021 14:55:44 -0700 Subject: [PATCH 08/56] build_win: Don't report an error on help switch --- build_win.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/build_win.bat b/build_win.bat index 34fea8d617..c03ebf0399 100644 --- a/build_win.bat +++ b/build_win.bat @@ -65,6 +65,7 @@ SET PS_DESTDIR= CALL :RESOLVE_DESTDIR_CACHE REM Set up parameters used by help menu +SET EXIT_STATUS=0 SET PS_CONFIG_DEFAULT=%PS_CONFIG% SET PS_ARCH_HOST=%PS_ARCH% (echo " -help /help -h /h -? /? ")| findstr /I /C:" %~1 ">nul && GOTO :HELP From 2f5f35bbefdf958a0cb7576b084027a6010d5af3 Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Fri, 13 Aug 2021 13:46:28 -0700 Subject: [PATCH 09/56] Update Windows build docs for build_win.bat --- doc/How to build - Windows.md | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index 42d559c5a7..d0da05d7b5 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -3,7 +3,7 @@ ### Install the tools Install Visual Studio Community 2019 from [visualstudio.microsoft.com/vs/](https://visualstudio.microsoft.com/vs/). Older versions are not supported as PrusaSlicer requires support for C++17. -Select all workload options for C++ +Select all workload options for C++ and make sure to launch Visual Studio after install (to ensure that the full setup completes). Install git for Windows from [gitforwindows.org](https://gitforwindows.org/) Download and run the exe accepting all defaults @@ -17,6 +17,42 @@ c:> cd src c:\src> git clone https://github.com/prusa3d/PrusaSlicer.git ``` +### Run the automatic build script + +The script `build_win.bat` will automatically find the default Visual Studio installation, set up the build environment, and then run both CMake and MSBuild to generate the dependencies and application as needed. If you'd rather do these steps manually, you can skip to the [Manual Build Instructions](#manual-build-instructions) in the next section. Otherwise, just run the following command to get everything going with the default configs: + +``` +c:\src>cd c:\src\PrusaSlicer +c:\src\PrusaSlicer>build_win.bat -d=..\PrusaSlicer-deps -r=console +``` + +The build script will run for a while (over an hour, depending on your machine) and automatically perform the following steps: +1. Configure and build [deps](#compile-the-dependencies) as RelWithDebInfo with `c:\src\PrusaSlicer-deps` as the destination directory +2. Configure and build all [application targets](#compile-prusaslicer) as RelWithDebInfo +3. Launch the resulting `prusa-slicer-console.exe` binary + +You can change the above command line options to do things like: +* Change the destination for the dependencies by pointing `-d` to a different directory such as: `build_win.bat -d=s:\PrusaSlicerDeps` +* Open the solution in Visual Studio after the build completes by changing the `-r` switch to `-r=ide` +* Generate a release build without debug info by adding `-c=Release` or a full debug build with `-c=Debug` +* Perform an incremental application build (the default) with: `build_win.bat -s=app-dirty` +* Clean and rebuild the application: `build_win.bat -s=app` +* Clean and rebuild the dependencies: `build_win.bat -s=deps` +* Clean and rebuild everything (app and deps): `build_win.bat -s=all` +* _The full list of build script options can be listed by running:_ `build_win.bat -?` + +### Troubleshooting + +You're best off initiating builds from within Visual Studio for day-to-day development. However, the `build_win.bat` script can be very helpful if you run into build failures after updating your source tree. Here are some tips to keep in mind: +* The last several lines of output from `build_win.bat` will usually have the most helpful error messages. +* If CMake complains about missing binaries or paths (e.g. after updating Visual Studio), building with `build_win.bat` will force CMake to regenerate its cache on an error. +* After a deps change, you may just need to rebuild everything with the `-s=all` switch. +* Reading through the instructions in the next section may help diagnose more complex issues. + +# Manual Build Instructions + +_Follow the steps below if you want to understand how to perform a manual build, or if you're troubleshooting issues with the automatic build script._ + ### Compile the dependencies. Dependencies are updated seldomly, thus they are compiled out of the PrusaSlicer source tree. Go to the Windows Start Menu and Click on "Visual Studio 2019" folder, then select the ->"x64 Native Tools Command Prompt" to open a command window and run the following: From fa7b2f351f1293940e37f1e5659d709c6dbc0d01 Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Wed, 20 Oct 2021 11:19:15 +0200 Subject: [PATCH 10/56] Fix for: ../src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp:102:46: error: control reaches end of non-void function [-Werror=return-type] src\slic3r\GUI\Gizmos\GLGizmoSimplify.cpp(103) : warning C4715: '::operator()': not all control paths return a value --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index f823afb6ff..2c094bf784 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -100,6 +100,7 @@ void GLGizmoSimplify::add_simplify_suggestion_notification( GLGizmoSimplify* simplify = dynamic_cast(manager.get_current()); if (simplify == nullptr) return close_notification; simplify->set_center_position(); + return close_notification; }; manager.push_simplify_suggestion_notification( t, objects[object_id]->id(), hypertext, open_simplify); From dea188083c280742cd24f93531d268415ed22cf4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 20 Oct 2021 12:50:21 +0200 Subject: [PATCH 11/56] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Fixed update of sidebar object manipulation reset buttons after using a gizmo --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 0c8f161a9b..1a2cb6be1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -721,6 +721,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); processed = true; } +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + else if (evt.LeftUp()) { + selection.stop_dragging(); + wxGetApp().obj_manipul()->set_dirty(); + } +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS } else { // mouse inside toolbar From 22e63af0ceb3d641d5284b27515893e9fd00c593 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 20 Oct 2021 13:07:54 +0200 Subject: [PATCH 12/56] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Fixed update of sidebar object manipulation reset buttons after editing object's transformation fields --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 09492db094..c279fad903 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -820,6 +820,9 @@ void ObjectManipulation::change_position_value(int axis, double value) Selection& selection = canvas->get_selection(); selection.start_dragging(); selection.translate(position - m_cache.position, selection.requires_local_axes()); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + selection.stop_dragging(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS canvas->do_move(L("Set Position")); m_cache.position = position; @@ -851,6 +854,9 @@ void ObjectManipulation::change_rotation_value(int axis, double value) selection.rotate( (M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation), transformation_type); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + selection.stop_dragging(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS canvas->do_rotate(L("Set Orientation")); m_cache.rotation = rotation; @@ -923,6 +929,9 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const selection.start_dragging(); selection.scale(scaling_factor, transformation_type); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + selection.stop_dragging(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale")); } From 79ff8a0dda91af5028c8d6540e6bc2a168794599 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 20 Oct 2021 13:19:15 +0200 Subject: [PATCH 13/56] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Follow-up of dea188083c280742cd24f93531d268415ed22cf4 and 22e63af0ceb3d641d5284b27515893e9fd00c593. Added missing calls to Selection::stop_dragging() --- src/slic3r/GUI/GLCanvas3D.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b0a83d93f1..b44fa167a3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2568,12 +2568,15 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) if (camera_space) { Eigen::Matrix inv_view_3x3 = wxGetApp().plater()->get_camera().get_view_matrix().inverse().matrix().block(0, 0, 3, 3); displacement = multiplier * (inv_view_3x3 * direction); - displacement(2) = 0.0; + displacement.z() = 0.0; } else displacement = multiplier * direction; m_selection.translate(displacement); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_selection.stop_dragging(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS m_dirty = true; } ); @@ -2672,6 +2675,9 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) auto do_rotate = [this](double angle_z_rad) { m_selection.start_dragging(); m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + m_selection.stop_dragging(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS m_dirty = true; // wxGetApp().obj_manipul()->set_dirty(); }; From 39a6c13c8167ffdbbf72d55e042eea77ebaf15c1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 20 Oct 2021 14:34:22 +0200 Subject: [PATCH 14/56] Use proper morphological closing for pad creation. --- src/libslic3r/SLA/ConcaveHull.cpp | 42 ++++++------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp index 1724089894..cfce36d162 100644 --- a/src/libslic3r/SLA/ConcaveHull.cpp +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -40,36 +40,6 @@ Point ConcaveHull::centroid(const Points &pp) return c; } -// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound -// mode -template -static ClipperLib::Paths fast_offset(PolygonsProvider &&paths, - coord_t delta, - ClipperLib::JoinType jointype) -{ - using ClipperLib::ClipperOffset; - using ClipperLib::etClosedPolygon; - using ClipperLib::Paths; - using ClipperLib::Path; - - ClipperOffset offs; - offs.ArcTolerance = scaled(0.01); - - for (auto &p : paths) - // If the input is not at least a triangle, we can not do this algorithm - if(p.size() < 3) { - BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; - return {}; - } - - offs.AddPaths(std::forward(paths), jointype, etClosedPolygon); - - Paths result; - offs.Execute(result, static_cast(delta)); - - return result; -} - Points ConcaveHull::calculate_centroids() const { // We get the centroids of all the islands in the 2D slice @@ -158,15 +128,17 @@ ExPolygons ConcaveHull::to_expolygons() const ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta) { - ExPolygons ret = ClipperPaths_to_Slic3rExPolygons( - fast_offset(fast_offset(ClipperUtils::PolygonsProvider(hull.polygons()), 2 * delta, ClipperLib::jtRound), -delta, ClipperLib::jtRound)); - for (ExPolygon &p : ret) p.holes.clear(); - return ret; + return to_expolygons(offset_waffle_style(hull, delta)); } Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta) { - return to_polygons(offset_waffle_style_ex(hull, delta)); + Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound); + + auto it = std::remove_if(res.begin(), res.end(), [](Polygon &p) { return p.is_clockwise(); }); + res.erase(it, res.end()); + + return res; } }} // namespace Slic3r::sla From ed67fb506e2885dd9772d02358ceb493fc7c40f3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 20 Oct 2021 15:08:33 +0200 Subject: [PATCH 15/56] Fix some warnings on gcc 11 --- src/PrusaSlicer.cpp | 2 +- src/libnest2d/include/libnest2d/placers/nfpplacer.hpp | 2 +- src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 0da5e73808..41d5231f16 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -705,7 +705,7 @@ bool CLI::setup(int argc, char **argv) // Initialize with defaults. for (const t_optiondef_map *options : { &cli_actions_config_def.options, &cli_transform_config_def.options, &cli_misc_config_def.options }) - for (const std::pair &optdef : *options) + for (const t_optiondef_map::value_type &optdef : *options) m_config.option(optdef.first, true); set_data_dir(m_config.opt_string("datadir")); diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 47ba7bbdc5..00f6a999fb 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -577,7 +577,7 @@ private: template - Shapes calcnfp(const Item &trsh, Level) + Shapes calcnfp(const Item &/*trsh*/, Level) { // Function for arbitrary level of nfp implementation // TODO: implement diff --git a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp index dc1bbd4f1f..486996f0d6 100644 --- a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp @@ -33,7 +33,8 @@ public: PackResult(Item& item): item_ptr_(&item), move_(item.translation()), - rot_(item.rotation()) {} + rot_(item.rotation()), + overfit_(1.0) {} PackResult(double overfit = 1.0): item_ptr_(nullptr), overfit_(overfit) {} From 6887fa829ee4b059ae4366605bda5a8f12a91e77 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 20 Oct 2021 15:48:39 +0200 Subject: [PATCH 16/56] Another warning fix for gcc 11 --- src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 1bc5489145..43ac958b59 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1216,7 +1216,7 @@ DynamicConfig SLAPrintStatistics::config() const DynamicConfig SLAPrintStatistics::placeholders() { DynamicConfig config; - for (const std::string &key : { + for (const char *key : { "print_time", "total_cost", "total_weight", "objects_used_material", "support_used_material" }) config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); From 482841b39e4210a6d737fb3b059f0c55a61a5114 Mon Sep 17 00:00:00 2001 From: Justin Schuh Date: Fri, 23 Jul 2021 07:16:21 -0700 Subject: [PATCH 17/56] Disable acceleration control if default_acceleration is zero --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ff0a7c0273..116fcbe7fb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2807,7 +2807,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, gcode += this->unretract(); // adjust acceleration - { + if (m_config.default_acceleration.value > 0) { double acceleration; if (this->on_first_layer() && m_config.first_layer_acceleration.value > 0) { acceleration = m_config.first_layer_acceleration.value; From 97d3c3e00afd801809fe90e9683d218cf8434cb8 Mon Sep 17 00:00:00 2001 From: Hannes Hauswedell Date: Sun, 21 Feb 2021 17:58:17 +0000 Subject: [PATCH 18/56] GLGizmoHollow: Increase hole diameter slider range (PR #6101) --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index d45a2e6137..614e838114 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -621,7 +621,7 @@ RENDER_AGAIN: ImGui::PushItemWidth(window_width - diameter_slider_left); float diam = 2.f * m_new_hole_radius; - m_imgui->slider_float("##hole_diameter", &diam, 1.f, 15.f, "%.1f mm", 1.f, false); + m_imgui->slider_float("##hole_diameter", &diam, 1.f, 25.f, "%.1f mm", 1.f, false); // Let's clamp the value (which could have been entered by keyboard) to a larger range // than the slider. This allows entering off-scale values and still protects against //complete non-sense. From fc5560aac2a093948b51860c8a23458c37bb6381 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 20 Oct 2021 21:59:07 +0200 Subject: [PATCH 19/56] Fix of #3270 (Confusing cooling hint when min and max fan speed are equal) --- src/slic3r/GUI/PresetHints.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 5c5ed26124..ce709d9eb8 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -34,11 +34,17 @@ std::string PresetHints::cooling_description(const Preset &preset) "so that no less than %3%s are spent on that layer " "(however, speed will never be reduced below %4%mm/s)."), slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed); - if (fan_below_layer_time > slowdown_below_layer_time) - out += "\n" + - GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, " + if (fan_below_layer_time > slowdown_below_layer_time) { + out += "\n"; + if (min_fan_speed != max_fan_speed) + out += GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, " "fan will run at a proportionally decreasing speed between %2%%% and %3%%%."), fan_below_layer_time, max_fan_speed, min_fan_speed); + else + out += GUI::format(_L("If estimated layer time is greater, but still below ~%1%s, " + "fan will run at %2%%%"), + fan_below_layer_time, min_fan_speed); + } out += "\n"; } if (preset.config.opt_bool("fan_always_on", 0)) { From d8a0d0523fa4ca4862f60c1c68a40339d2ab1854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 21 Oct 2021 08:29:56 +0200 Subject: [PATCH 20/56] Allowed painting in the FDM supports, seam, and multi-material gizmo to only triangles not clipped by a clipping plane. --- src/libslic3r/TriangleSelector.cpp | 56 +++++++++++--------- src/libslic3r/TriangleSelector.hpp | 42 ++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 40 ++++++++++---- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp | 2 + src/slic3r/GUI/MeshUtils.hpp | 1 + 5 files changed, 95 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index f65292f03f..1699786887 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -128,13 +128,13 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, const Vec3f& source, float radius, CursorType cursor_type, EnforcerBlockerType new_state, const Transform3d& trafo, const Transform3d& trafo_no_translate, - bool triangle_splitting, float highlight_by_angle_deg) + bool triangle_splitting, const ClippingPlane &clp, float highlight_by_angle_deg) { assert(facet_start < m_orig_size_indices); // Save current cursor center, squared radius and camera direction, so we don't // have to pass it around. - m_cursor = Cursor(hit, source, radius, cursor_type, trafo); + m_cursor = Cursor(hit, source, radius, cursor_type, trafo, clp); // In case user changed cursor size since last time, update triangle edge limit. // It is necessary to compare the internal radius in m_cursor! radius is in @@ -172,15 +172,23 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start, } } -void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, - const Transform3d& trafo_no_translate, - float seed_fill_angle, float highlight_by_angle_deg, +bool TriangleSelector::is_facet_clipped(int facet_idx, const ClippingPlane &clp) const +{ + for (int vert_idx : m_triangles[facet_idx].verts_idxs) + if (clp.is_active() && clp.is_mesh_point_clipped(m_vertices[vert_idx].v)) + return true; + + return false; +} + +void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate, + const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg, bool force_reselection) { assert(facet_start < m_orig_size_indices); - // Recompute seed fill only if the cursor is pointing on facet unselected by seed fill. - if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection) + // Recompute seed fill only if the cursor is pointing on facet unselected by seed fill or a clipping plane is active. + if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active()) return; this->seed_fill_unselect_all_triangles(); @@ -215,7 +223,7 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st // Propagate over the original triangles. for (int neighbor_idx : m_neighbors[current_facet]) { assert(neighbor_idx >= -1); - if (neighbor_idx >= 0 && !visited[neighbor_idx]) { + if (neighbor_idx >= 0 && !visited[neighbor_idx] && !is_facet_clipped(neighbor_idx, clp)) { // Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do. const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle]; const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle]; @@ -331,12 +339,12 @@ void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int ver process_subtriangle(touching.second, Partition::Second); } -void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate, bool force_reselection) +void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, const ClippingPlane &clp, bool propagate, bool force_reselection) { int start_facet_idx = select_unsplit_triangle(hit, facet_start); assert(start_facet_idx != -1); - // Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill. - if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection)) + // Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill or a clipping plane is active. + if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active())) return; assert(!m_triangles[start_facet_idx].is_split()); @@ -379,7 +387,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_ std::vector touching_triangles = get_all_touching_triangles(current_facet, neighbors[current_facet], neighbors_propagated[current_facet]); for(const int tr_idx : touching_triangles) { - if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state) + if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state || is_facet_clipped(tr_idx, clp)) continue; assert(!m_triangles[tr_idx].is_split()); @@ -1687,11 +1695,12 @@ void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_stat TriangleSelector::Cursor::Cursor( const Vec3f& center_, const Vec3f& source_, float radius_world, - CursorType type_, const Transform3d& trafo_) + CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_) : center{center_}, source{source_}, type{type_}, - trafo{trafo_.cast()} + trafo{trafo_.cast()}, + clipping_plane(clipping_plane_) { Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor(); if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) { @@ -1714,22 +1723,19 @@ TriangleSelector::Cursor::Cursor( dir = (center - source).normalized(); } - // Is a point (in mesh coords) inside a cursor? -bool TriangleSelector::Cursor::is_mesh_point_inside(Vec3f point) const +bool TriangleSelector::Cursor::is_mesh_point_inside(const Vec3f &point) const { - if (! uniform_scaling) - point = trafo * point; + const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point); + const Vec3f diff = center - transformed_point; + const bool is_point_inside = (type == CIRCLE ? (diff - diff.dot(dir) * dir).squaredNorm() : diff.squaredNorm()) < radius_sqr; - Vec3f diff = center - point; - return (type == CIRCLE ? - (diff - diff.dot(dir) * dir).squaredNorm() : - diff.squaredNorm()) - < radius_sqr; + if (is_point_inside && clipping_plane.is_active()) + return !clipping_plane.is_mesh_point_clipped(point); + + return is_point_inside; } - - // p1, p2, p3 are in mesh coords! bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_, const Vec3f& p2_, diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index b3c468a6e2..b9f136c2e2 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -4,6 +4,7 @@ // #define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG +#include #include "Point.hpp" #include "TriangleMesh.hpp" @@ -22,6 +23,18 @@ public: POINTER }; + struct ClippingPlane + { + Vec3f normal; + float offset; + ClippingPlane() : normal{0.f, 0.f, 1.f}, offset{FLT_MAX} {}; + explicit ClippingPlane(const std::array &clp) : normal{clp[0], clp[1], clp[2]}, offset{clp[3]} {} + + bool is_active() const { return offset != FLT_MAX; } + + bool is_mesh_point_clipped(const Vec3f &point) const { return normal.dot(point) - offset > 0.f; } + }; + std::pair, std::vector> precompute_all_neighbors() const; void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector &neighbors_out, std::vector &neighbors_normal_out) const; @@ -47,19 +60,22 @@ public: const Transform3d &trafo, // matrix to get from mesh to world const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation bool triangle_splitting, // If triangles will be split base on the cursor or not + const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. - void seed_fill_select_triangles(const Vec3f &hit, // point where to start - int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation - float seed_fill_angle, // the maximal angle between two facets to be painted by the same color - float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. - bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle + void seed_fill_select_triangles(const Vec3f &hit, // point where to start + int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to + const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation + const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only + float seed_fill_angle, // the maximal angle between two facets to be painted by the same color + float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. + bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle - void bucket_fill_select_triangles(const Vec3f &hit, // point where to start - int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to - bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. - bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle + void bucket_fill_select_triangles(const Vec3f &hit, // point where to start + int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to + const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only + bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to. + bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle bool has_facets(EnforcerBlockerType state) const; static bool has_facets(const std::pair>, std::vector> &data, EnforcerBlockerType test_state); @@ -183,8 +199,8 @@ protected: struct Cursor { Cursor() = default; Cursor(const Vec3f& center_, const Vec3f& source_, float radius_world, - CursorType type_, const Transform3d& trafo_); - bool is_mesh_point_inside(Vec3f pt) const; + CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_); + bool is_mesh_point_inside(const Vec3f &pt) const; bool is_pointer_in_triangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) const; Vec3f center; @@ -195,6 +211,7 @@ protected: Transform3f trafo; Transform3f trafo_normal; bool uniform_scaling; + ClippingPlane clipping_plane; }; Cursor m_cursor; @@ -211,6 +228,7 @@ private: void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant. bool is_pointer_in_triangle(int facet_idx) const; bool is_edge_inside_cursor(int facet_idx) const; + bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const; int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0}); void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state); Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 8cfb3fbcaa..2825913d3c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -267,7 +267,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous const ModelObject *mo = m_c->selection_info()->model_object(); const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true); - m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle, + const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix(); + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); m_seed_fill_last_mesh_id = m_rr.mesh_id; @@ -364,22 +365,22 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast(); assert(m_rr.mesh_id < int(m_triangle_selectors.size())); + const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix); if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) { m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state); if (m_tool_type == ToolType::SMART_FILL) - m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle, + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) - m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false, true); + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false, true); else if (m_tool_type == ToolType::BUCKET_FILL) - m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true, true); + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true, true); m_seed_fill_last_mesh_id = -1; } else if (m_tool_type == ToolType::BRUSH) m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type, - new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled, + new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled, clp, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); - m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); m_last_mouse_click = mouse_position; } @@ -430,16 +431,18 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if(m_rr.mesh_id != m_seed_fill_last_mesh_id) seed_fill_unselect_all(); + const Transform3d &trafo_matrix = trafo_matrices[m_rr.mesh_id]; const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[m_rr.mesh_id]; assert(m_rr.mesh_id < int(m_triangle_selectors.size())); + const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix); if (m_tool_type == ToolType::SMART_FILL) - m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle, + m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) - m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false); + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false); else if (m_tool_type == ToolType::BUCKET_FILL) - m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true); + m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true); m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); m_seed_fill_last_mesh_id = m_rr.mesh_id; return true; @@ -569,6 +572,25 @@ void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&) m_schedule_update = true; } +TriangleSelector::ClippingPlane GLGizmoPainterBase::get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const { + const ::Slic3r::GUI::ClippingPlane *const clipping_plane = m_c->object_clipper()->get_clipping_plane(); + if (clipping_plane == nullptr || !clipping_plane->is_active()) + return {}; + + const Vec3d clp_normal = clipping_plane->get_normal(); + const double clp_offset = clipping_plane->get_offset(); + + const Transform3d trafo_normal = Transform3d(trafo.linear().transpose()); + const Transform3d trafo_inv = trafo.inverse(); + + Vec3d point_on_plane = clp_normal * clp_offset; + Vec3d point_on_plane_transformed = trafo_inv * point_on_plane; + Vec3d normal_transformed = trafo_normal * clp_normal; + auto offset_transformed = float(point_on_plane_transformed.dot(normal_transformed)); + + return TriangleSelector::ClippingPlane({float(normal_transformed.x()), float(normal_transformed.y()), float(normal_transformed.z()), offset_transformed}); +} + std::array TriangleSelectorGUI::get_seed_fill_color(const std::array &base_color) { return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f}; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 3093b0bec8..eb09715da4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -184,6 +184,8 @@ protected: ClippingPlaneDataWrapper get_clipping_plane_data() const; + TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const; + private: bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const; void update_raycast_cache(const Vec2d& mouse_position, diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 767abd7fd5..ccdb830420 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -53,6 +53,7 @@ public: m_data[2] = norm_dir.z(); } void set_offset(double offset) { m_data[3] = offset; } + double get_offset() const { return m_data[3]; } Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); } bool is_active() const { return m_data[3] != DBL_MAX; } static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); } From e0eac1f20c8d1dfef1182ae9f3b01df393e4e8c8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 21 Oct 2021 09:26:57 +0200 Subject: [PATCH 21/56] Tech ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - Fixed update of sidebar object manipulation reset buttons after using place on face gizmo --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1a2cb6be1f..316f800368 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -715,6 +715,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) } else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) { // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active +#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + selection.stop_dragging(); + wxGetApp().obj_manipul()->set_dirty(); +#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS processed = true; } else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) { From 5f19539df54f0572e54e63892fb1652359fa66c6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 21 Oct 2021 09:55:12 +0200 Subject: [PATCH 22/56] Obvious fixes to stupid warnings --- src/slic3r/GUI/DoubleSlider.cpp | 2 +- src/slic3r/GUI/GUI_ObjectList.cpp | 2 +- src/slic3r/GUI/ImGuiWrapper.cpp | 2 +- src/slic3r/GUI/OptionsGroup.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- tests/libslic3r/test_config.cpp | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index d0b29165c7..fc47484b56 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -2080,7 +2080,7 @@ void Control::auto_color_change() } int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt(); - int extruder = 2; +// int extruder = 2; const Print& print = GUI::wxGetApp().plater()->fff_print(); for (auto object : print.objects()) { diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 83f9d1ec1a..43ebd23f2b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1433,7 +1433,7 @@ void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false selection_changed(); } /* -void ObjectList::load_part(ModelObject& model_object, std::vector& added_volumes, ModelVolumeType type, bool from_galery/* = false* /) +void ObjectList::load_part(ModelObject& model_object, std::vector& added_volumes, ModelVolumeType type, bool from_galery = false) { if (type != ModelVolumeType::MODEL_PART) return; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 22dccc695a..a65991a1e6 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -306,7 +306,7 @@ ImVec2 ImGuiWrapper::get_item_spacing() const { const ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; - return g.Style.ItemSpacing; + return style.ItemSpacing; } float ImGuiWrapper::get_slider_float_height() const diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index fd9f4b5e82..2e0d8384de 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -201,7 +201,7 @@ void OptionsGroup::activate_line(Line& line) if (line.is_separator()) return; - m_use_custom_ctrl_as_parent = false; + m_use_custom_ctrl_as_parent = false; if (line.full_width && ( line.widget != nullptr || diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9a24d91cc8..0013e106c7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -205,7 +205,7 @@ void ObjectInfo::msw_rescale() void ObjectInfo::update_warning_icon(const std::string& warning_icon_name) { - if (showing_manifold_warning_icon = !warning_icon_name.empty()) { + if ((showing_manifold_warning_icon = !warning_icon_name.empty())) { m_warning_icon_name = warning_icon_name; manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name)); } diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index 2131050844..8dfda789ee 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -220,7 +220,7 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") { cereal::BinaryOutputArchive oarchive(ss); oarchive(cfg); serialized = ss.str(); - } catch (std::runtime_error e) { + } catch (std::runtime_error &e) { e.what(); } @@ -230,7 +230,7 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") { std::stringstream ss(serialized); cereal::BinaryInputArchive iarchive(ss); iarchive(cfg2); - } catch (std::runtime_error e) { + } catch (std::runtime_error &e) { e.what(); } REQUIRE(cfg == cfg2); From 69721123db6e63c30f0fb42c9c10da83f054d6e0 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Fri, 8 Oct 2021 21:12:39 +0100 Subject: [PATCH 23/56] [fuzzy skin] Use "walls" instead of "perimeters" in option "Perimeters" has a specific meaning within Prusa Slicer, but fuzzy skin uses "perimeters" to mean "walls" which can be confusing --- src/libslic3r/PrintConfig.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b34a6cb58e..2e3de778e0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1244,8 +1244,8 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("external"); def->enum_values.push_back("all"); def->enum_labels.push_back(L("None")); - def->enum_labels.push_back(L("External perimeters")); - def->enum_labels.push_back(L("All perimeters")); + def->enum_labels.push_back(L("Outside walls")); + def->enum_labels.push_back(L("All walls")); def->mode = comSimple; def->set_default_value(new ConfigOptionEnum(FuzzySkinType::None)); From 1a9a4088c2e0e8cb5609d46188d78499cb15c28c Mon Sep 17 00:00:00 2001 From: Filip Sykala Date: Thu, 21 Oct 2021 11:01:15 +0200 Subject: [PATCH 24/56] Fix translation suggestion by @Oleksandra Iushchenko --- src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 2c094bf784..c72f744e10 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -267,7 +267,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi ImGui::NewLine(); ImGui::SameLine(m_gui_cfg->bottom_left_width); - ImGui::Text(_L("%d triangles").c_str(), m_configuration.wanted_count); + ImGui::Text(_u8L("%d triangles").c_str(), m_configuration.wanted_count); m_imgui->disabled_end(); // use_count if (ImGui::Checkbox(_u8L("Show wireframe").c_str(), &m_show_wireframe)) { @@ -277,7 +277,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi bool is_canceling = m_state == State::canceling; m_imgui->disabled_begin(is_canceling); - if (m_imgui->button(_L("Cancel"))) { + if (m_imgui->button(_u8L("Cancel"))) { if (m_state == State::settings) { if (m_original_its.has_value()) { set_its(*m_original_its); @@ -296,7 +296,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi bool is_processing = m_state != State::settings; m_imgui->disabled_begin(is_processing); - if (m_imgui->button(_L("Apply"))) { + if (m_imgui->button(_u8L("Apply"))) { if (!m_is_valid_result) { m_state = State::close_on_end; process(); @@ -397,7 +397,7 @@ void GLGizmoSimplify::process() // store previous state auto plater = wxGetApp().plater(); - plater->take_snapshot(_L("Simplify ") + m_volume->name); + plater->take_snapshot(_u8L("Simplify ") + m_volume->name); plater->clear_before_change_mesh(m_obj_index); } From 3f1fc8329b4b63b8bad0d36976218423e32ab404 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 21 Oct 2021 11:33:55 +0200 Subject: [PATCH 25/56] Follow-up to 482841b, see also #6743: The validation is now counterproductive, both the backend and the UI can handle the situation well enough. --- src/libslic3r/PrintConfig.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 2e3de778e0..232e724e76 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3970,10 +3970,16 @@ std::string validate(const FullPrintConfig &cfg) if (em <= 0) return "Invalid value for --extrusion-multiplier"; + // The following test was commented out after 482841b, see also https://github.com/prusa3d/PrusaSlicer/pull/6743. + // The backend should now handle this case correctly. I.e., zero default_acceleration behaves as if all others + // were zero too. This is now consistent with what the UI said would happen. + // The UI already grays the fields out, there is no more reason to reject it here. This function validates the + // config before exporting, leaving this check in would mean that config would be rejected before export + // (although both the UI and the backend handle it). // --default-acceleration - if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) && - cfg.default_acceleration == 0.) - return "Invalid zero value for --default-acceleration when using other acceleration settings"; + //if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) && + // cfg.default_acceleration == 0.) + // return "Invalid zero value for --default-acceleration when using other acceleration settings"; // --spiral-vase if (cfg.spiral_vase) { From dc588e2db56a0a2377cb27885819bf7c43c4ceaf Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 21 Oct 2021 11:42:00 +0200 Subject: [PATCH 26/56] Pass previously set arc tolerance to concave hull --- src/libslic3r/SLA/ConcaveHull.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/ConcaveHull.cpp b/src/libslic3r/SLA/ConcaveHull.cpp index cfce36d162..08a2ff676a 100644 --- a/src/libslic3r/SLA/ConcaveHull.cpp +++ b/src/libslic3r/SLA/ConcaveHull.cpp @@ -133,7 +133,8 @@ ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta) Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta) { - Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound); + auto arc_tolerance = scaled(0.01); + Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound, arc_tolerance); auto it = std::remove_if(res.begin(), res.end(), [](Polygon &p) { return p.is_clockwise(); }); res.erase(it, res.end()); From 767f401adaeb3558e25702b0f036abb98605b834 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 21 Oct 2021 13:35:20 +0200 Subject: [PATCH 27/56] Brim generator: Using pftNonZero instead of pftEvenOdd for intersection / difference. --- src/libslic3r/Brim.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index a13d578a36..8f31b0695c 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -114,6 +114,7 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print, clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true); // Execute union operation to construct polytree ClipperLib_Z::PolyTree islands_polytree; + //FIXME likely pftNonZero or ptfPositive would be better. Why are we using ptfEvenOdd for Unions? clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); std::unordered_set processed_objects_idx; @@ -486,7 +487,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true); clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true); // perform operation - clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); + clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); } // Second, trim the extrusion loops with the trimming regions. @@ -515,7 +516,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true); // perform operation ClipperLib_Z::PolyTree loops_trimmed_tree; - clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); + clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero); ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed); } From 7967997c7eaef76d05b6703187a31bb70ce168ae Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 22 Oct 2021 11:37:02 +0200 Subject: [PATCH 28/56] InfoDialog: Small code refactoring --- src/slic3r/GUI/MsgDialog.cpp | 41 +++--------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 41846500de..9f3f34f844 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -218,47 +218,12 @@ InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& : MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title) , msg(msg) { - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - // Text shown as HTML, so that mouse selection and Ctrl-V to copy will work. - wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); - { - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - wxFont monospace = wxGetApp().code_font(); - wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); - auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); - const int font_size = font.GetPointSize() - 1; - int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size }; - html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size); - html->SetBorders(2); - - // calculate html page size from text - int lines = msg.Freq('\n'); - - if (msg.Contains("")) { - int pos = 0; - while (pos < (int)msg.Len() && pos != wxNOT_FOUND) { - pos = msg.find("", pos + 1); - lines+=2; - } - } - int page_height = std::min((font.GetPixelSize().y + 1) * lines, 68 * wxGetApp().em_unit()); - wxSize page_size(68 * wxGetApp().em_unit(), page_height); - - html->SetMinSize(page_size); - - std::string msg_escaped = xml_escape(msg.ToUTF8().data(), true); - boost::replace_all(msg_escaped, "\r\n", "
"); - boost::replace_all(msg_escaped, "\n", "
"); - html->SetPage("" + wxString::FromUTF8(msg_escaped.data()) + ""); - content_sizer->Add(html, 1, wxEXPAND); - } - + add_msg_content(this, content_sizer, msg); // Set info bitmap logo->SetBitmap(create_scaled_bitmap("info", this, 84)); + wxGetApp().UpdateDlgDarkUI(this); + Fit(); } From 0791a2b3edc5cdf6e37d681d55727796c1b48c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Thu, 21 Oct 2021 10:57:02 +0200 Subject: [PATCH 29/56] Fix of #7104 (dimples in perimeters after multi-material segmentation) --- src/libslic3r/MultiMaterialSegmentation.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index b17ee000ad..86b3488b08 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1324,6 +1324,18 @@ static inline std::vector> mmu_segmentation_top_and_bott } } + auto filter_out_small_polygons = [&num_extruders, &num_layers](std::vector> &raw_surfaces, double min_area) -> void { + for (size_t extruder_idx = 0; extruder_idx < num_extruders; ++extruder_idx) + if (!raw_surfaces[extruder_idx].empty()) + for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) + if (!raw_surfaces[extruder_idx][layer_idx].empty()) + remove_small(raw_surfaces[extruder_idx][layer_idx], min_area); + }; + + // Filter out polygons less than 0.1mm^2, because they are unprintable and causing dimples on outer primers (#7104) + filter_out_small_polygons(top_raw, Slic3r::sqr(scale_(0.1f))); + filter_out_small_polygons(bottom_raw, Slic3r::sqr(scale_(0.1f))); + #ifdef MMU_SEGMENTATION_DEBUG_TOP_BOTTOM { const char* colors[] = { "aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "yellow" }; From b6b5bdb5929f229d0188a7df1191f92a8c87facd Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 22 Oct 2021 12:57:31 +0200 Subject: [PATCH 30/56] Fixed wrong showing of the default context menu if right button was clicked on empty space when several objects are selected in 3DScene --- src/slic3r/GUI/Plater.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0013e106c7..c6de7df228 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4178,8 +4178,11 @@ void Plater::priv::on_right_click(RBtnEvent& evt) wxMenu* menu = nullptr; if (obj_idx == -1) { // no one or several object are selected - if (evt.data.second) // right button was clicked on empty space + if (evt.data.second) { // right button was clicked on empty space + if (!get_selection().is_empty()) // several objects are selected in 3DScene + return; menu = menus.default_menu(); + } else menu = menus.multi_selection_menu(); } From 59502e7861d43fba99cf08ff51fe5529b8e253ef Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Oct 2021 14:47:23 +0200 Subject: [PATCH 31/56] Seam placement improvements --- src/libslic3r/ExtrusionEntity.cpp | 24 ++-- src/libslic3r/ExtrusionEntity.hpp | 1 + src/libslic3r/GCode.cpp | 27 ++--- src/libslic3r/GCode/SeamPlacer.cpp | 189 +++++++++++++++++++++++------ src/libslic3r/GCode/SeamPlacer.hpp | 31 ++++- src/libslic3r/MultiPoint.cpp | 4 +- 6 files changed, 203 insertions(+), 73 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index d9320373f1..7b2506a221 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -193,12 +193,8 @@ bool ExtrusionLoop::split_at_vertex(const Point &point) return false; } -// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging. -void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) +std::pair ExtrusionLoop::get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const { - if (this->paths.empty()) - return; - // Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for. size_t path_idx = 0; Point p; @@ -207,15 +203,15 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) Point p_non_overhang; size_t path_idx_non_overhang = 0; double min_non_overhang = std::numeric_limits::max(); - for (const ExtrusionPath &path : this->paths) { + for (const ExtrusionPath& path : this->paths) { Point p_tmp = point.projection_onto(path.polyline); double dist = (p_tmp - point).cast().norm(); if (dist < min) { p = p_tmp; min = dist; path_idx = &path - &this->paths.front(); - } - if (prefer_non_overhang && ! is_bridge(path.role()) && dist < min_non_overhang) { + } + if (prefer_non_overhang && !is_bridge(path.role()) && dist < min_non_overhang) { p_non_overhang = p_tmp; min_non_overhang = dist; path_idx_non_overhang = &path - &this->paths.front(); @@ -224,9 +220,19 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) if (prefer_non_overhang && min_non_overhang != std::numeric_limits::max()) { // Only apply the non-overhang point if there is one. path_idx = path_idx_non_overhang; - p = p_non_overhang; + p = p_non_overhang; } } + return std::make_pair(path_idx, p); +} + +// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging. +void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) +{ + if (this->paths.empty()) + return; + + auto [path_idx, p] = get_closest_path_and_point(point, prefer_non_overhang); // now split path_idx in two parts const ExtrusionPath &path = this->paths[path_idx]; diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 2f35083169..1c990f5eaf 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -258,6 +258,7 @@ public: double length() const override; bool split_at_vertex(const Point &point); void split_at(const Point &point, bool prefer_non_overhang); + std::pair get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const; void clip_end(double distance, ExtrusionPaths* paths) const; // Test, whether the point is extruded by a bridging flow. // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 116fcbe7fb..137ae18472 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2508,28 +2508,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // extrude all loops ccw bool was_clockwise = loop.make_counter_clockwise(); - SeamPosition seam_position = m_config.seam_position; - if (loop.loop_role() == elrSkirt) - seam_position = spNearest; - // find the point of the loop that is closest to the current extruder position // or randomize if requested Point last_pos = this->last_pos(); if (m_config.spiral_vase) { loop.split_at(last_pos, false); - } else { - const EdgeGrid::Grid* edge_grid_ptr = (lower_layer_edge_grid && *lower_layer_edge_grid) - ? lower_layer_edge_grid->get() - : nullptr; - Point seam = m_seam_placer.get_seam(*m_layer, seam_position, loop, - last_pos, EXTRUDER_CONFIG(nozzle_diameter), - (m_layer == NULL ? nullptr : m_layer->object()), - was_clockwise, edge_grid_ptr); - // Split the loop at the point with a minium penalty. - if (!loop.split_at_vertex(seam)) - // The point is not in the original loop. Insert it. - loop.split_at(seam, true); } + else + m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first, EXTRUDER_CONFIG(nozzle_diameter)); // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so @@ -2652,7 +2638,14 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector(region.perimeters.begin(), region.perimeters.end()), + *m_layer, m_config.seam_position, + m_config.external_perimeters_first, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), + (m_layer == NULL ? nullptr : m_layer->object()), + (lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr)); + + for (const ExtrusionEntity* ee : region.perimeters) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } return gcode; diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 878a9ef587..609ad4d2d8 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -292,11 +292,154 @@ void SeamPlacer::init(const Print& print) -Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position, - const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr, - const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid) +void SeamPlacer::plan_perimeters(const std::vector perimeters, + const Layer& layer, SeamPosition seam_position, bool external_first, + Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid) { + // When printing the perimeters, we want the seams on external and internal perimeters to match. + // We have a list of perimeters in the order to be printed. Each internal perimeter must inherit + // the seam from the previous external perimeter. + + m_plan.clear(); + m_plan_idx = 0; + + if (perimeters.empty() || ! po) + return; + + m_plan.resize(perimeters.size()); + + for (int i = 0; i < int(perimeters.size()); ++i) { + if (perimeters[i]->role() == erExternalPerimeter && perimeters[i]->is_loop()) { + last_pos = this->calculate_seam( + layer, seam_position, *dynamic_cast(perimeters[i]), nozzle_dmr, + po, lower_layer_edge_grid, last_pos); + m_plan[i].external = true; + } + m_plan[i].pt = last_pos; + } +} + + +void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter) +{ + const double seam_offset = nozzle_diameter; + + Point seam = last_pos; + if (! m_plan.empty() && m_plan_idx < m_plan.size()) { + if (m_plan[m_plan_idx].external) + seam = m_plan[m_plan_idx].pt; + else if (! external_first) { + // Internal perimeter printed before the external. + // First get list of external seams. + std::vector ext_seams; + for (size_t i = 0; i < m_plan.size(); ++i) { + if (m_plan[i].external) + ext_seams.emplace_back(i); + } + + if (! ext_seams.empty()) { + // First find the line segment closest to an external seam: + int path_idx = 0; + int line_idx = 0; + size_t ext_seam_idx = -1; + double min_dist_sqr = std::numeric_limits::max(); + double nozzle_diameter_sqr = std::pow(scale_(nozzle_diameter), 2.); + std::vector lines_vect; + for (int i = 0; i < loop.paths.size(); ++i) { + lines_vect.emplace_back(loop.paths[i].polyline.lines()); + const Lines& lines = lines_vect.back(); + for (int j = 0; j < lines.size(); ++j) { + for (size_t k : ext_seams) { + double d_sqr = lines[j].distance_to_squared(m_plan[k].pt); + if (d_sqr < min_dist_sqr) { + path_idx = i; + line_idx = j; + ext_seam_idx = k; + min_dist_sqr = d_sqr; + } + } + } + } + + // Only accept seam that is reasonably close. + double limit_dist_sqr = std::pow(double(scale_((ext_seam_idx - m_plan_idx) * nozzle_diameter * 2.)), 2.); + if (ext_seam_idx != -1 && min_dist_sqr < limit_dist_sqr) { + // Now find a projection of the external seam + const Lines& lines = lines_vect[path_idx]; + Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]); + double dist = (closest.cast() - lines[line_idx].b.cast()).norm(); + + // And walk along the perimeter until we make enough space for + // seams of all perimeters beforethe external one. + double offset = (ext_seam_idx - m_plan_idx) * scale_(seam_offset); + double last_offset = offset; + offset -= dist; + const Point* a = &closest; + const Point* b = &lines[line_idx].b; + while (++line_idx < lines.size() && offset > 0.) { + last_offset = offset; + offset -= lines[line_idx].length(); + a = &lines[line_idx].a; + b = &lines[line_idx].b; + } + + // We have walked far enough, too far maybe. Interpolate on the + // last segment to find the end precisely. + offset = std::min(0., offset); // In case that offset is still positive (we may have "wrapped around") + double ratio = last_offset / (last_offset - offset); + seam = (a->cast() + ((b->cast() - a->cast()) * ratio)).cast(); + } + } + } + else { + // We should have a candidate ready from before. If not, use last_pos. + if (m_plan_idx > 0 && m_plan[m_plan_idx - 1].precalculated) + seam = m_plan[m_plan_idx - 1].pt; + } + } + + + // Split the loop at the point with a minium penalty. + if (!loop.split_at_vertex(seam)) + // The point is not in the original loop. Insert it. + loop.split_at(seam, true); + + if (external_first && m_plan_idx+1 1) { + const ExtrusionPath& last = loop.paths.back(); + auto it = last.polyline.points.crbegin() + 1; + for (; it != last.polyline.points.crend(); ++it) { + running_sqr += (it->cast() - (it - 1)->cast()).squaredNorm(); + if (running_sqr > dist_sqr) + break; + running_sqr_last = running_sqr; + } + if (running_sqr <= dist_sqr) + it = last.polyline.points.crend() - 1; + // Now interpolate. + double ratio = (std::sqrt(dist_sqr) - std::sqrt(running_sqr_last)) / (std::sqrt(running_sqr) - std::sqrt(running_sqr_last)); + m_plan[m_plan_idx + 1].pt = ((it - 1)->cast() + (it->cast() - (it - 1)->cast()) * std::min(ratio, 1.)).cast(); + m_plan[m_plan_idx + 1].precalculated = true; + } + } + + ++m_plan_idx; +} + + +// Returns a seam for an EXTERNAL perimeter. +Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_position, + const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos) +{ + assert(loop.role() == erExternalPerimeter); Polygon polygon = loop.polygon(); + bool was_clockwise = polygon.make_counter_clockwise(); BoundingBox polygon_bb = polygon.bounding_box(); const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5); @@ -438,7 +581,7 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position, } } - if (seam_position == spAligned && loop.role() == erExternalPerimeter) + if (seam_position == spAligned) m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb); @@ -466,42 +609,8 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position, #endif return polygon.points[idx_min]; - } else { // spRandom - if (po->print()->default_region_config().external_perimeters_first) { - if (loop.role() == erExternalPerimeter) - last_pos = this->get_random_seam(layer_idx, polygon, po_idx); - else { - // Internal perimeters will just use last_pos. - } - } else { - if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) { - // This loop does not contain any other loop. Set a random position. - // The other loops will get a seam close to the random point chosen - // on the innermost contour. - last_pos = this->get_random_seam(layer_idx, polygon, po_idx); - m_last_loop_was_external = false; - } - if (loop.role() == erExternalPerimeter) { - if (m_last_loop_was_external) { - // There was no internal perimeter before this one. - last_pos = this->get_random_seam(layer_idx, polygon, po_idx); - } else { - if (is_custom_seam_on_layer(layer_idx, po_idx)) { - // There is a possibility that the loop will be influenced by custom - // seam enforcer/blocker. In this case do not inherit the seam - // from internal loops (which may conflict with the custom selection - // and generate another random one. - bool saw_custom = false; - Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom); - if (saw_custom) - last_pos = candidate; - } - } - m_last_loop_was_external = true; - } - } - return last_pos; - } + } else + return this->get_random_seam(layer_idx, polygon, po_idx); } diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 0bae7af5a9..5545fcdf90 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -2,7 +2,9 @@ #define libslic3r_SeamPlacer_hpp_ #include +#include +#include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/BoundingBox.hpp" @@ -41,16 +43,29 @@ class SeamPlacer { public: void init(const Print& print); - Point get_seam(const Layer& layer, const SeamPosition seam_position, - const ExtrusionLoop& loop, Point last_pos, - coordf_t nozzle_diameter, const PrintObject* po, - bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid); + // When perimeters are printed, first call this function with the respective + // external perimeter. SeamPlacer will find a location for its seam and remember it. + // Subsequent calls to get_seam will return this position. + + + void plan_perimeters(const std::vector perimeters, + const Layer& layer, SeamPosition seam_position, bool external_first, + Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid); + + void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter); + using TreeType = AABBTreeIndirect::Tree<2, coord_t>; using AlignedBoxType = Eigen::AlignedBox; private: + // When given an external perimeter (!), returns the seam. + Point calculate_seam(const Layer& layer, const SeamPosition seam_position, + const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos); + struct CustomTrianglesPerLayer { Polygons polys; TreeType tree; @@ -61,7 +76,13 @@ private: coordf_t m_last_print_z = -1.; const PrintObject* m_last_po = nullptr; - bool m_last_loop_was_external = true; + struct SeamPoint { + Point pt; + bool precalculated = false; + bool external = false; + }; + std::vector m_plan; + size_t m_plan_idx; std::vector> m_enforcers; std::vector> m_blockers; diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 03f7ff59c4..aa8295098c 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -47,9 +47,9 @@ void MultiPoint::rotate(double angle, const Point ¢er) double MultiPoint::length() const { - Lines lines = this->lines(); + const Lines& lines = this->lines(); double len = 0; - for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) { + for (auto it = lines.cbegin(); it != lines.cend(); ++it) { len += it->length(); } return len; From 19eb984d72a27e4b3bd2147f43e3f3e792a4aeae Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 19 Oct 2021 12:05:23 +0200 Subject: [PATCH 32/56] SeamPlacer: one more heuristic to get rid of long travels --- src/libslic3r/GCode.cpp | 6 +++--- src/libslic3r/GCode/SeamPlacer.cpp | 29 ++++++++++++++++++++--------- src/libslic3r/GCode/SeamPlacer.hpp | 8 ++++++-- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 137ae18472..97599ca071 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2515,7 +2515,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou loop.split_at(last_pos, false); } else - m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first, EXTRUDER_CONFIG(nozzle_diameter)); + m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first, + EXTRUDER_CONFIG(nozzle_diameter), lower_layer_edge_grid ? lower_layer_edge_grid->get() : nullptr); // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so @@ -2640,8 +2641,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector(region.perimeters.begin(), region.perimeters.end()), - *m_layer, m_config.seam_position, - m_config.external_perimeters_first, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), + *m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), (m_layer == NULL ? nullptr : m_layer->object()), (lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr)); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 609ad4d2d8..6d082a431a 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -293,7 +293,7 @@ void SeamPlacer::init(const Print& print) void SeamPlacer::plan_perimeters(const std::vector perimeters, - const Layer& layer, SeamPosition seam_position, bool external_first, + const Layer& layer, SeamPosition seam_position, Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, const EdgeGrid::Grid* lower_layer_edge_grid) { @@ -315,20 +315,32 @@ void SeamPlacer::plan_perimeters(const std::vector perim layer, seam_position, *dynamic_cast(perimeters[i]), nozzle_dmr, po, lower_layer_edge_grid, last_pos); m_plan[i].external = true; + m_plan[i].seam_position = seam_position; + m_plan[i].layer = &layer; + m_plan[i].po = po; } m_plan[i].pt = last_pos; } } -void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter) +void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter, + const EdgeGrid::Grid* lower_layer_edge_grid) { const double seam_offset = nozzle_diameter; Point seam = last_pos; if (! m_plan.empty() && m_plan_idx < m_plan.size()) { - if (m_plan[m_plan_idx].external) + if (m_plan[m_plan_idx].external) { seam = m_plan[m_plan_idx].pt; + // One more heuristics: if the seam is too far from current nozzle position, + // try to place it again. This can happen in cases where the external perimeter + // does not belong to the preceding ones and they are ordered so they end up + // far from each other. + if ((seam.cast() - last_pos.cast()).squaredNorm() > std::pow(scale_(5.*nozzle_diameter), 2.)) + seam = this->calculate_seam(*m_plan[m_plan_idx].layer, m_plan[m_plan_idx].seam_position, loop, nozzle_diameter, + m_plan[m_plan_idx].po, lower_layer_edge_grid, last_pos); + } else if (! external_first) { // Internal perimeter printed before the external. // First get list of external seams. @@ -342,14 +354,13 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext // First find the line segment closest to an external seam: int path_idx = 0; int line_idx = 0; - size_t ext_seam_idx = -1; + size_t ext_seam_idx = size_t(-1); double min_dist_sqr = std::numeric_limits::max(); - double nozzle_diameter_sqr = std::pow(scale_(nozzle_diameter), 2.); std::vector lines_vect; - for (int i = 0; i < loop.paths.size(); ++i) { + for (int i = 0; i < int(loop.paths.size()); ++i) { lines_vect.emplace_back(loop.paths[i].polyline.lines()); const Lines& lines = lines_vect.back(); - for (int j = 0; j < lines.size(); ++j) { + for (int j = 0; j < int(lines.size()); ++j) { for (size_t k : ext_seams) { double d_sqr = lines[j].distance_to_squared(m_plan[k].pt); if (d_sqr < min_dist_sqr) { @@ -364,7 +375,7 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext // Only accept seam that is reasonably close. double limit_dist_sqr = std::pow(double(scale_((ext_seam_idx - m_plan_idx) * nozzle_diameter * 2.)), 2.); - if (ext_seam_idx != -1 && min_dist_sqr < limit_dist_sqr) { + if (ext_seam_idx != size_t(-1) && min_dist_sqr < limit_dist_sqr) { // Now find a projection of the external seam const Lines& lines = lines_vect[path_idx]; Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]); @@ -377,7 +388,7 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext offset -= dist; const Point* a = &closest; const Point* b = &lines[line_idx].b; - while (++line_idx < lines.size() && offset > 0.) { + while (++line_idx < int(lines.size()) && offset > 0.) { last_offset = offset; offset -= lines[line_idx].length(); a = &lines[line_idx].a; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 5545fcdf90..57c3532c3b 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -49,11 +49,12 @@ public: void plan_perimeters(const std::vector perimeters, - const Layer& layer, SeamPosition seam_position, bool external_first, + const Layer& layer, SeamPosition seam_position, Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, const EdgeGrid::Grid* lower_layer_edge_grid); - void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter); + void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter, + const EdgeGrid::Grid* lower_layer_edge_grid); using TreeType = AABBTreeIndirect::Tree<2, coord_t>; @@ -80,6 +81,9 @@ private: Point pt; bool precalculated = false; bool external = false; + const Layer* layer = nullptr; + SeamPosition seam_position; + const PrintObject* po = nullptr; }; std::vector m_plan; size_t m_plan_idx; From 00dfb8f69cb1dc565b53f6a21be2d28f9fb9b8b9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 22 Oct 2021 13:15:42 +0200 Subject: [PATCH 33/56] Make sure that we have the lower_layer_edge_grid when placing seams --- src/libslic3r/GCode.cpp | 49 ++++++++++++++++++++++---------------- src/slic3r/GUI/GUI_App.cpp | 1 + 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 97599ca071..8f8d8dbe0b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2479,31 +2479,36 @@ std::string GCode::change_layer(coordf_t print_z) +static std::unique_ptr calculate_layer_edge_grid(const Layer& layer) +{ + auto out = make_unique(); + + // Create the distance field for a layer below. + const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); + out->create(layer.lslices, distance_field_resolution); + out->calculate_sdf(); +#if 0 + { + static int iRun = 0; + BoundingBox bbox = (*lower_layer_edge_grid)->bbox(); + bbox.min(0) -= scale_(5.f); + bbox.min(1) -= scale_(5.f); + bbox.max(0) += scale_(5.f); + bbox.max(1) += scale_(5.f); + EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); + } +#endif + return out; +} + + std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr *lower_layer_edge_grid) { // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation - if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) { - if (! *lower_layer_edge_grid) { - // Create the distance field for a layer below. - const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); - *lower_layer_edge_grid = make_unique(); - (*lower_layer_edge_grid)->create(m_layer->lower_layer->lslices, distance_field_resolution); - (*lower_layer_edge_grid)->calculate_sdf(); - #if 0 - { - static int iRun = 0; - BoundingBox bbox = (*lower_layer_edge_grid)->bbox(); - bbox.min(0) -= scale_(5.f); - bbox.min(1) -= scale_(5.f); - bbox.max(0) += scale_(5.f); - bbox.max(1) += scale_(5.f); - EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); - } - #endif - } - } + if (m_layer->lower_layer && lower_layer_edge_grid != nullptr && ! *lower_layer_edge_grid) + *lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer); // extrude all loops ccw bool was_clockwise = loop.make_counter_clockwise(); @@ -2640,6 +2645,10 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorlower_layer && ! lower_layer_edge_grid) + lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer); + m_seam_placer.plan_perimeters(std::vector(region.perimeters.begin(), region.perimeters.end()), *m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), (m_layer == NULL ? nullptr : m_layer->object()), diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 7d6940bd0d..349f179309 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -673,6 +673,7 @@ void GUI_App::post_init() // to popup a modal dialog on start without screwing combo boxes. // This is ugly but I honestly found no better way to do it. // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + assert(this->preset_updater); // FIXME Following condition is probably not neccessary. if (this->preset_updater) { this->check_updates(false); CallAfter([this] { From decdaa82d3db9619b45d3c59d5106526cf65d57d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 Oct 2021 13:51:33 +0200 Subject: [PATCH 34/56] Modified implementation of Line::parallel_to(const Line& line) and Line::perpendicular_to(const Line& line) --- src/libslic3r/Line.cpp | 14 ++++++++++++++ src/libslic3r/Line.hpp | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 1a96b8b1f4..78ca2b9af4 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -63,11 +63,25 @@ bool Line::parallel_to(double angle) const return Slic3r::Geometry::directions_parallel(this->direction(), angle); } +bool Line::parallel_to(const Line& line) const +{ + const Vec2d v1 = (this->b - this->a).cast(); + const Vec2d v2 = (line.b - line.a).cast(); + return std::fabs(cross2(v1, v2)) < EPSILON; +} + #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS bool Line::perpendicular_to(double angle) const { return Slic3r::Geometry::directions_perpendicular(this->direction(), angle); } + +bool Line::perpendicular_to(const Line& line) const +{ + const Vec2d v1 = (this->b - this->a).cast(); + const Vec2d v2 = (line.b - line.a).cast(); + return std::fabs(v1.dot(v2)) < EPSILON; +} #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS bool Line::intersection(const Line &l2, Point *intersection) const diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 4a33c7c9c1..a4dcd827ca 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -104,10 +104,10 @@ public: double distance_to(const Point &point) const { return distance_to(point, this->a, this->b); } double perp_distance_to(const Point &point) const; bool parallel_to(double angle) const; - bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); } + bool parallel_to(const Line& line) const; #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS bool perpendicular_to(double angle) const; - bool perpendicular_to(const Line& line) const { return this->perpendicular_to(line.direction()); } + bool perpendicular_to(const Line& line) const; #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); } double orientation() const; From f9e3e1d9fd7e8bc4b9c5b9385e4a2d4105498b5c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 21 Oct 2021 13:39:27 +0200 Subject: [PATCH 35/56] Fixed spelling error get_standart_duration -> get_standard_duration --- src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/NotificationManager.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 40f5c31169..a984b6ba75 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1509,7 +1509,7 @@ void NotificationManager::push_notification(NotificationType type, std::function callback, int timestamp) { - int duration = get_standart_duration(level); + int duration = get_standard_duration(level); push_notification_data({ type, level, duration, text, hypertext, callback }, timestamp); } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 2031586b8d..9adcc240d0 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -713,7 +713,7 @@ private: void sort_notifications(); // If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed. bool has_slicing_error_notification(); - size_t get_standart_duration(NotificationLevel level) + size_t get_standard_duration(NotificationLevel level) { switch (level) { From 48e89e0b0d3eb20ce0dfedfb85ad702978c1877a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Oct 2021 11:31:16 +0200 Subject: [PATCH 36/56] const replaced with satic constexpr const --- src/slic3r/Utils/PresetUpdater.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/PresetUpdater.hpp b/src/slic3r/Utils/PresetUpdater.hpp index d0d18a7d88..085aaa4a6a 100644 --- a/src/slic3r/Utils/PresetUpdater.hpp +++ b/src/slic3r/Utils/PresetUpdater.hpp @@ -13,7 +13,7 @@ class AppConfig; class PresetBundle; class Semver; -const int SLIC3R_VERSION_BODY_MAX = 256; +static constexpr const int SLIC3R_VERSION_BODY_MAX = 256; class PresetUpdater { From d43ae66ecaaac762b5ee452e6d500bbb81ccc496 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Oct 2021 14:02:39 +0200 Subject: [PATCH 37/56] reduced copy / paste redudancy by extracting a new function to produce convex hull: its_convex_hull() --- src/libslic3r/TriangleMesh.cpp | 164 +++++++++++++++++---------------- src/libslic3r/TriangleMesh.hpp | 3 + src/slic3r/GUI/3DScene.cpp | 108 ++-------------------- 3 files changed, 97 insertions(+), 178 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 09cb893727..3d1b3d6897 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -462,85 +462,9 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafo, d TriangleMesh TriangleMesh::convex_hull_3d() const { - // The qhull call: - orgQhull::Qhull qhull; - qhull.disableOutputStream(); // we want qhull to be quiet - std::vector src_vertices; - try - { -#if REALfloat - qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt"); -#else - src_vertices.reserve(this->its.vertices.size() * 3); - // We will now fill the vector with input points for computation: - for (const stl_vertex &v : this->its.vertices) - for (int i = 0; i < 3; ++ i) - src_vertices.emplace_back(v(i)); - qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); -#endif - } - catch (...) - { - std::cout << "Unable to create convex hull" << std::endl; - return TriangleMesh(); - } - - // Let's collect results: - std::vector dst_vertices; - std::vector dst_facets; - // Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices). - std::vector map_dst_vertices; -#ifndef NDEBUG - Vec3f centroid = Vec3f::Zero(); - for (const stl_vertex& pt : this->its.vertices) - centroid += pt; - centroid /= float(this->its.vertices.size()); -#endif // NDEBUG - for (const orgQhull::QhullFacet facet : qhull.facetList()) { - // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID. - Vec3i indices; - int cnt = 0; - for (const orgQhull::QhullVertex vertex : facet.vertices()) { - int id = vertex.id(); - assert(id >= 0); - if (id >= int(map_dst_vertices.size())) - map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1); - if (int i = map_dst_vertices[id]; i == -1) { - // Allocate a new vertex. - i = int(dst_vertices.size()); - map_dst_vertices[id] = i; - orgQhull::QhullPoint pt(vertex.point()); - dst_vertices.emplace_back(pt[0], pt[1], pt[2]); - indices[cnt] = i; - } else { - // Reuse existing vertex. - indices[cnt] = i; - } - if (cnt ++ == 3) - break; - } - assert(cnt == 3); - if (cnt == 3) { - // QHull sorts vertices of a face lexicographically by their IDs, not by face normals. - // Calculate face normal based on the order of vertices. - Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]); - auto *n2 = facet.getBaseT()->normal; - auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2]; -#ifndef NDEBUG - Vec3f n3 = (dst_vertices[indices(0)] - centroid); - auto d3 = n.dot(n3); - assert((d < 0.f) == (d3 < 0.f)); -#endif // NDEBUG - // Get the face normal from QHull. - if (d < 0.f) - // Fix face orientation. - std::swap(indices[1], indices[2]); - dst_facets.emplace_back(indices); - } - } - - TriangleMesh mesh{ std::move(dst_vertices), std::move(dst_facets) }; - assert(mesh.stats().manifold()); + TriangleMesh mesh(its_convex_hull(this->its)); + // Quite often qhull produces non-manifold mesh. + // assert(mesh.stats().manifold()); return mesh; } @@ -1108,6 +1032,88 @@ indexed_triangle_set its_make_sphere(double radius, double fa) return mesh; } +indexed_triangle_set its_convex_hull(const std::vector &pts) +{ + std::vector dst_vertices; + std::vector dst_facets; + + if (! pts.empty()) { + // The qhull call: + orgQhull::Qhull qhull; + qhull.disableOutputStream(); // we want qhull to be quiet + std::vector src_vertices; + try { + #if REALfloat + qhull.runQhull("", 3, (int)pts.size(), (const realT*)(pts.front().data()), "Qt"); + #else + src_vertices.reserve(this->its.vertices.size() * 3); + // We will now fill the vector with input points for computation: + for (const stl_vertex &v : this->its.vertices) + for (int i = 0; i < 3; ++ i) + src_vertices.emplace_back(v(i)); + qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); + #endif + } catch (...) { + BOOST_LOG_TRIVIAL(error) << "its_convex_hull: Unable to create convex hull"; + return {}; + } + + // Let's collect results: + // Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices). + std::vector map_dst_vertices; + #ifndef NDEBUG + Vec3f centroid = Vec3f::Zero(); + for (const stl_vertex& pt : this->its.vertices) + centroid += pt; + centroid /= float(this->its.vertices.size()); + #endif // NDEBUG + for (const orgQhull::QhullFacet facet : qhull.facetList()) { + // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID. + Vec3i indices; + int cnt = 0; + for (const orgQhull::QhullVertex vertex : facet.vertices()) { + int id = vertex.id(); + assert(id >= 0); + if (id >= int(map_dst_vertices.size())) + map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1); + if (int i = map_dst_vertices[id]; i == -1) { + // Allocate a new vertex. + i = int(dst_vertices.size()); + map_dst_vertices[id] = i; + orgQhull::QhullPoint pt(vertex.point()); + dst_vertices.emplace_back(pt[0], pt[1], pt[2]); + indices[cnt] = i; + } else { + // Reuse existing vertex. + indices[cnt] = i; + } + if (cnt ++ == 3) + break; + } + assert(cnt == 3); + if (cnt == 3) { + // QHull sorts vertices of a face lexicographically by their IDs, not by face normals. + // Calculate face normal based on the order of vertices. + Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]); + auto *n2 = facet.getBaseT()->normal; + auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2]; + #ifndef NDEBUG + Vec3f n3 = (dst_vertices[indices(0)] - centroid); + auto d3 = n.dot(n3); + assert((d < 0.f) == (d3 < 0.f)); + #endif // NDEBUG + // Get the face normal from QHull. + if (d < 0.f) + // Fix face orientation. + std::swap(indices[1], indices[2]); + dst_facets.emplace_back(indices); + } + } + } + + return { std::move(dst_facets), std::move(dst_vertices) }; +} + void its_reverse_all_facets(indexed_triangle_set &its) { for (stl_triangle_vertex_indices &face : its.indices) diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index ec6401982f..951e351fee 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -301,6 +301,9 @@ indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360)); indexed_triangle_set its_make_pyramid(float base, float height); indexed_triangle_set its_make_sphere(double radius, double fa); +indexed_triangle_set its_convex_hull(const std::vector &pts); +inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); } + inline TriangleMesh make_cube(double x, double y, double z) { return TriangleMesh(its_make_cube(x, y, z)); } inline TriangleMesh make_prism(float width, float length, float height) { return TriangleMesh(its_make_prism(width, length, height)); } inline TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)) { return TriangleMesh{its_make_cylinder(r, h, fa)}; } diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 26a64fdb78..78e32b1cd0 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -40,12 +40,6 @@ #include -#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS -#include -#include -#include -#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS - #ifdef HAS_GLSAFE void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char* function_name) { @@ -626,100 +620,16 @@ void GLVolume::render_sinking_contours() #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS void GLVolume::calc_convex_hull_3d() { - if (this->indexed_vertex_array.vertices_and_normals_interleaved.empty()) - return; - - TriangleMesh mesh; - for (size_t i = 0; i < this->indexed_vertex_array.vertices_and_normals_interleaved.size(); i += 6) { - const size_t v_id = 3 + i; - mesh.its.vertices.push_back({ this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 0], - this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 1], - this->indexed_vertex_array.vertices_and_normals_interleaved[v_id + 2] - }); + const std::vector &src = this->indexed_vertex_array.vertices_and_normals_interleaved; + std::vector pts; + pts.reserve(src.size() / 6); + for (auto it = src.begin() + 3;;) { + pts.push_back({ *it, *(it + 1), *(it + 2) }); + it += 3; + if (it == src.end()) + break; } - - const std::vector& vertices = mesh.its.vertices; - - // The qhull call: - orgQhull::Qhull qhull; - qhull.disableOutputStream(); // we want qhull to be quiet - std::vector src_vertices; - try - { -#if REALfloat - qhull.runQhull("", 3, (int)vertices.size(), (const realT*)(vertices.front().data()), "Qt"); -#else - src_vertices.reserve(vertices.size() * 3); - // We will now fill the vector with input points for computation: - for (const stl_vertex& v : vertices) - for (int i = 0; i < 3; ++i) - src_vertices.emplace_back(v(i)); - qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); -#endif - } - catch (...) - { - std::cout << "GLVolume::calc_convex_hull_3d() - Unable to create convex hull" << std::endl; - return ; - } - - // Let's collect results: - std::vector dst_vertices; - std::vector dst_facets; - // Map of QHull's vertex ID to our own vertex ID (pointing to dst_vertices). - std::vector map_dst_vertices; -#ifndef NDEBUG - Vec3f centroid = Vec3f::Zero(); - for (const auto& pt : vertices) - centroid += pt; - centroid /= float(vertices.size()); -#endif // NDEBUG - for (const orgQhull::QhullFacet& facet : qhull.facetList()) { - // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID. - Vec3i indices; - int cnt = 0; - for (const orgQhull::QhullVertex vertex : facet.vertices()) { - const int id = vertex.id(); - assert(id >= 0); - if (id >= int(map_dst_vertices.size())) - map_dst_vertices.resize(next_highest_power_of_2(size_t(id + 1)), -1); - if (int i = map_dst_vertices[id]; i == -1) { - // Allocate a new vertex. - i = int(dst_vertices.size()); - map_dst_vertices[id] = i; - orgQhull::QhullPoint pt(vertex.point()); - dst_vertices.emplace_back(pt[0], pt[1], pt[2]); - indices[cnt] = i; - } - else - // Reuse existing vertex. - indices[cnt] = i; - - if (cnt++ == 3) - break; - } - assert(cnt == 3); - if (cnt == 3) { - // QHull sorts vertices of a face lexicographically by their IDs, not by face normals. - // Calculate face normal based on the order of vertices. - const Vec3f n = (dst_vertices[indices(1)] - dst_vertices[indices(0)]).cross(dst_vertices[indices(2)] - dst_vertices[indices(1)]); - auto* n2 = facet.getBaseT()->normal; - const auto d = n.x() * n2[0] + n.y() * n2[1] + n.z() * n2[2]; -#ifndef NDEBUG - const Vec3f n3 = (dst_vertices[indices(0)] - centroid); - const auto d3 = n.dot(n3); - assert((d < 0.f) == (d3 < 0.f)); -#endif // NDEBUG - // Get the face normal from QHull. - if (d < 0.f) - // Fix face orientation. - std::swap(indices[1], indices[2]); - dst_facets.emplace_back(indices); - } - } - - TriangleMesh out_mesh{ std::move(dst_vertices), std::move(dst_facets) }; - this->set_convex_hull(out_mesh); + this->set_convex_hull(TriangleMesh(its_convex_hull(pts))); } #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS From d34c9cec9a31faffc9af82f5d4317599b2e147e1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 Oct 2021 14:29:56 +0200 Subject: [PATCH 38/56] Follow-up of decdaa82d3db9619b45d3c59d5106526cf65d57d - Use normalized vectors --- src/libslic3r/Line.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 78ca2b9af4..773df34424 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -67,7 +67,7 @@ bool Line::parallel_to(const Line& line) const { const Vec2d v1 = (this->b - this->a).cast(); const Vec2d v2 = (line.b - line.a).cast(); - return std::fabs(cross2(v1, v2)) < EPSILON; + return sqr(cross2(v1, v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm(); } #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS @@ -80,7 +80,7 @@ bool Line::perpendicular_to(const Line& line) const { const Vec2d v1 = (this->b - this->a).cast(); const Vec2d v2 = (line.b - line.a).cast(); - return std::fabs(v1.dot(v2)) < EPSILON; + return sqr(v1.dot(v2)) < sqr(EPSILON) * v1.squaredNorm() * v2.squaredNorm(); } #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS From 4fd9a99f5b5906853b1cd840e2f3fa33e53dc2f1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 22 Oct 2021 14:46:07 +0200 Subject: [PATCH 39/56] Bed3D::is_circle() - Use Geometry::circle_center_taubin_newton() to detect candidate center --- src/slic3r/GUI/3DBed.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 18c017da11..8e68673116 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -271,7 +271,8 @@ bool Bed3D::is_circle(const Pointfs& shape, Vec2d* center, double* radius) // Analyze the array of points. // Do they reside on a circle ? - const Vec2d box_center = BoundingBoxf(shape).center(); + const Vec2d box_center = Geometry::circle_center_taubin_newton(shape); + std::vector vertex_distances; double avg_dist = 0.0; for (const Vec2d& pt : shape) { From b15db6fdb72ec88160c3846855e011c417a384a3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Oct 2021 15:11:42 +0200 Subject: [PATCH 40/56] Fixed previous commit, which broke debug compilation. --- src/libslic3r/TriangleMesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 3d1b3d6897..ee88fdb636 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1063,9 +1063,9 @@ indexed_triangle_set its_convex_hull(const std::vector &pts) std::vector map_dst_vertices; #ifndef NDEBUG Vec3f centroid = Vec3f::Zero(); - for (const stl_vertex& pt : this->its.vertices) + for (const stl_vertex& pt : pts) centroid += pt; - centroid /= float(this->its.vertices.size()); + centroid /= float(pts.size()); #endif // NDEBUG for (const orgQhull::QhullFacet facet : qhull.facetList()) { // Collect face vertices first, allocate unique vertices in dst_vertices based on QHull's vertex ID. From 3021a6f57f8f4491965e01e7ff478f7354094d00 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Oct 2021 15:20:40 +0200 Subject: [PATCH 41/56] Fixing previous commit for QHull configured for doubles, not floats. --- src/libslic3r/TriangleMesh.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index ee88fdb636..fb2621225a 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1041,14 +1041,16 @@ indexed_triangle_set its_convex_hull(const std::vector &pts) // The qhull call: orgQhull::Qhull qhull; qhull.disableOutputStream(); // we want qhull to be quiet + #if ! REALfloat std::vector src_vertices; + #endif try { #if REALfloat qhull.runQhull("", 3, (int)pts.size(), (const realT*)(pts.front().data()), "Qt"); #else - src_vertices.reserve(this->its.vertices.size() * 3); + src_vertices.reserve(pts.size() * 3); // We will now fill the vector with input points for computation: - for (const stl_vertex &v : this->its.vertices) + for (const stl_vertex &v : pts) for (int i = 0; i < 3; ++ i) src_vertices.emplace_back(v(i)); qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); From 8d0a4c7cd642b2047c55309c321f228e6ef3ae06 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 22 Oct 2021 15:35:51 +0200 Subject: [PATCH 42/56] SendSystemInfoDialog: Use button instead of hyperlink + Fixed wsWidgets asserts --- src/slic3r/GUI/SendSystemInfoDialog.cpp | 34 +++++++++++++------------ 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/SendSystemInfoDialog.cpp b/src/slic3r/GUI/SendSystemInfoDialog.cpp index 106617e769..f92060bfc8 100644 --- a/src/slic3r/GUI/SendSystemInfoDialog.cpp +++ b/src/slic3r/GUI/SendSystemInfoDialog.cpp @@ -67,8 +67,9 @@ public: SendSystemInfoDialog(wxWindow* parent); private: - bool send_info(); + wxString send_info(); const std::string m_system_info_json; + wxButton* m_btn_show_data; wxButton* m_btn_send; wxButton* m_btn_dont_send; wxButton* m_btn_ask_later; @@ -89,7 +90,7 @@ public: auto* btn = new wxButton(this, wxID_CANCEL, _L("Cancel")); auto* vsizer = new wxBoxSizer(wxVERTICAL); auto *top_sizer = new wxBoxSizer(wxVERTICAL); - vsizer->Add(text, 1, wxEXPAND|wxALIGN_CENTER_HORIZONTAL); + vsizer->Add(text, 1, wxEXPAND); vsizer->AddSpacer(5); vsizer->Add(btn, 0, wxALIGN_CENTER_HORIZONTAL); top_sizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT | wxBOTTOM, 10); @@ -548,7 +549,6 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) "installation are sent. PrusaSlicer is open source, if you want to " "inspect the code actually performing the communication, see %1%."), std::string("") + filename + ""); - wxString label3 = _L("Show verbatim data that will be sent"); auto* html_window = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxSize(70*em, 34*em), wxHW_SCROLLBAR_NEVER); wxString html = GUI::format_wxstr( @@ -560,17 +560,14 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) + text1 + "

" "" + "" + label2 + "
" - + text2 + "

" - + "" + label3 + "
" + + text2 + "", bgr_clr_str, text_clr_str); html_window->SetPage(html); - html_window->Bind(wxEVT_HTML_LINK_CLICKED, [this](wxHtmlLinkEvent&) { - ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7)); - dlg.ShowModal(); - }); vsizer->Add(html_window, 1, wxEXPAND); + m_btn_show_data = new wxButton(this, wxID_ANY, _L("Show verbatim data that will be sent")); + m_btn_ask_later = new wxButton(this, wxID_ANY, _L("Ask me next time")); m_btn_dont_send = new wxButton(this, wxID_ANY, _L("Do not send anything")); m_btn_send = new wxButton(this, wxID_ANY, _L("Send system info")); @@ -582,6 +579,7 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) hsizer->AddSpacer(em); hsizer->Add(m_btn_send); + vsizer->Add(m_btn_show_data, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 20); vsizer->Add(hsizer, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10); topSizer->Add(vsizer, 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, 10); @@ -599,9 +597,15 @@ SendSystemInfoDialog::SendSystemInfoDialog(wxWindow* parent) CenterOnParent(); + m_btn_show_data->Bind(wxEVT_BUTTON, [this](wxEvent&) { + ShowJsonDialog dlg(this, m_system_info_json, GetSize().Scale(0.9, 0.7)); + dlg.ShowModal(); + }); + m_btn_send->Bind(wxEVT_BUTTON, [this](const wxEvent&) { - if (send_info()) { + if (wxString out = send_info(); !out.IsEmpty()) { + InfoDialog(nullptr, wxEmptyString, out).ShowModal(); save_version(); EndModal(0); } @@ -630,7 +634,7 @@ void SendSystemInfoDialog::on_dpi_changed(const wxRect&) // This actually sends the info. -bool SendSystemInfoDialog::send_info() +wxString SendSystemInfoDialog::send_info() { std::atomic job_done = false; // Flag to communicate between threads. struct Result { @@ -674,11 +678,9 @@ bool SendSystemInfoDialog::send_info() job_done = true; // In case the user closed the dialog, let the other thread know sending_thread.join(); // and wait until it terminates. - if (result.value != Result::Cancelled) { // user knows he cancelled, no need to tell him. - InfoDialog info_dlg(wxGetApp().mainframe, wxEmptyString, result.str); - info_dlg.ShowModal(); - } - return result.value == Result::Success; + if (result.value == Result::Cancelled) + return ""; + return result.str; } From 7e780380eee2fb046a44753e3b34501ae1011b36 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Oct 2021 16:54:21 +0200 Subject: [PATCH 43/56] Fix of d43ae66ecaaac762b5ee452e6d500bbb81ccc496 reduced copy / paste redudancy by extracting a new function to produce convex hull: its_convex_hull() --- src/slic3r/GUI/3DScene.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 78e32b1cd0..3dd0c0e1fe 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -622,12 +622,12 @@ void GLVolume::calc_convex_hull_3d() { const std::vector &src = this->indexed_vertex_array.vertices_and_normals_interleaved; std::vector pts; + assert(src.size() % 6 == 0); pts.reserve(src.size() / 6); - for (auto it = src.begin() + 3;;) { + for (auto it = src.begin(); it != src.end(); ) { + it += 3; pts.push_back({ *it, *(it + 1), *(it + 2) }); it += 3; - if (it == src.end()) - break; } this->set_convex_hull(TriangleMesh(its_convex_hull(pts))); } From 22f3682c0f9e5dc5678d9653d3c9dc7ec54b8f6f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 22 Oct 2021 17:04:08 +0200 Subject: [PATCH 44/56] Fix of #7155 (broken loading of configs from <=1.41.3) --- src/libslic3r/Format/3mf.cpp | 7 ++++++- src/libslic3r/Format/AMF.cpp | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index b323145d3e..3271e2c384 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -916,7 +916,12 @@ namespace Slic3r { } //FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment. // Each config line is prefixed with a semicolon (G-code comment), that is ugly. - config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule); + + // Replacing the legacy function with load_from_ini_string_commented leads to issues when + // parsing 3MFs from before PrusaSlicer 2.0.0 (which can have duplicated entries in the INI. + // See https://github.com/prusa3d/PrusaSlicer/issues/7155. We'll revert it for now. + //config_substitutions.substitutions = config.load_from_ini_string_commented(std::move(buffer), config_substitutions.rule); + ConfigBase::load_from_gcode_string_legacy(config, buffer.data(), config_substitutions); } } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index bcc5c8b4a2..235cdecb5b 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -711,7 +711,12 @@ void AMFParserContext::endElement(const char * /* name */) if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) { //FIXME Loading a "will be one day a legacy format" of configuration in a form of a G-code comment. // Each config line is prefixed with a semicolon (G-code comment), that is ugly. - m_config_substitutions->substitutions = m_config->load_from_ini_string_commented(std::move(m_value[1].c_str()), m_config_substitutions->rule); + + // Replacing the legacy function with load_from_ini_string_commented leads to issues when + // parsing 3MFs from before PrusaSlicer 2.0.0 (which can have duplicated entries in the INI. + // See https://github.com/prusa3d/PrusaSlicer/issues/7155. We'll revert it for now. + //m_config_substitutions->substitutions = m_config->load_from_ini_string_commented(std::move(m_value[1].c_str()), m_config_substitutions->rule); + ConfigBase::load_from_gcode_string_legacy(*m_config, std::move(m_value[1].c_str()), *m_config_substitutions); } else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) { const char *opt_key = m_value[0].c_str() + 7; From 5ccf7e9e4756cb9a58b9aa344bde68aa8c7fa692 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Oct 2021 17:09:30 +0200 Subject: [PATCH 45/56] Updated version and config location to beta. --- src/slic3r/GUI/GUI_App.cpp | 3 ++- version.inc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 349f179309..b6b3ca91b6 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -746,7 +746,8 @@ void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. // SetAppName(SLIC3R_APP_KEY); - SetAppName(SLIC3R_APP_KEY "-alpha"); +// SetAppName(SLIC3R_APP_KEY "-alpha"); + SetAppName(SLIC3R_APP_KEY "-beta"); // SetAppDisplayName(SLIC3R_APP_NAME); // Set the Slic3r data directory at the Slic3r XS module. diff --git a/version.inc b/version.inc index fdbc2523f8..27900ee801 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.4.0-alpha3") +set(SLIC3R_VERSION "2.4.0-beta1") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_RC_VERSION "2,4,0,0") set(SLIC3R_RC_VERSION_DOTS "2.4.0.0") From 723155a15cb0821584ef005a8d62ea6988eb1754 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Sat, 23 Oct 2021 13:29:08 +0200 Subject: [PATCH 46/56] 1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S. --- resources/profiles/PrusaResearch.idx | 2 + resources/profiles/PrusaResearch.ini | 488 ++++++++++++++++++++++++++- 2 files changed, 489 insertions(+), 1 deletion(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index ee3a57fe85..cef8c654d2 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.4.0-beta0 +1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S. min_slic3r_version = 2.4.0-alpha0 1.4.0-alpha8 Added material profiles for Prusament Resin. Detect bridging perimeters enabled by default. 1.4.0-alpha7 Updated brim_separation value. Updated Prusa MINI end g-code. Added Filamentworld filament profiles. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 7d4ab7ccb2..e243963cef 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.4.0-alpha8 +config_version = 1.4.0-beta0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -2715,6 +2715,275 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 +[filament:Filatech FilaFlex30] +inherits = Filatech FilaFlex40 +temperature = 225 +filament_density = 1.15 +extrusion_multiplier = 1.1 +filament_cost = + +[filament:Filatech FilaFlex55] +inherits = Filatech FilaFlex40 +temperature = 230 +filament_density = 1.18 +bed_temperature = 60 +fan_always_on = 0 +fan_below_layer_time = 60 +filament_cost = +first_layer_temperature = 235 +extrusion_multiplier = 1 + +# [filament:Filatech TPE] +# inherits = Filatech FilaFlex40 +# first_layer_temperature = 230 +# temperature = 225 +# filament_density = 1.2 +# fan_below_layer_time = 60 +# max_fan_speed = 80 +# min_fan_speed = 80 +# fan_always_on = 1 + +[filament:Filatech TPU] +inherits = Filatech FilaFlex40 +first_layer_temperature = 230 +filament_density = 1.2 +fan_below_layer_time = 60 +max_fan_speed = 80 +min_fan_speed = 80 +fan_always_on = 1 +temperature = 235 + +[filament:Filatech ABS] +inherits = *ABSC* +filament_vendor = Filatech +filament_cost = +extrusion_multiplier = 0.95 +filament_density = 1.05 + +[filament:Filatech ABS @MINI] +inherits = Filatech ABS; *ABSMINI* + +[filament:Filatech FilaCarbon] +inherits = *ABSC* +filament_vendor = Filatech +filament_cost = +extrusion_multiplier = 0.95 +filament_density = 1.1 +first_layer_bed_temperature = 105 +bed_temperature = 100 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech FilaCarbon @MINI] +inherits = Filatech FilaCarbon; *ABSMINI* +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" + +[filament:Filatech FilaPLA] +inherits = *PLA* +filament_vendor = Filatech +filament_cost = +filament_density = 1.3 +first_layer_temperature = 235 +first_layer_bed_temperature = 50 +temperature = 230 +bed_temperature = 55 + +[filament:Filatech PLA] +inherits = *PLA* +filament_vendor = Filatech +filament_cost = +filament_density = 1.25 +first_layer_temperature = 215 +temperature = 210 + +[filament:Filatech PLA+] +inherits = Filatech PLA +filament_density = 1.24 + +[filament:Filatech FilaTough] +inherits = Filatech ABS +filament_cost = +extrusion_multiplier = 0.95 +filament_density = 1.29 +first_layer_temperature = 245 +first_layer_bed_temperature = 80 +temperature = 240 +bed_temperature = 90 +cooling = 0 +compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech HIPS] +inherits = Prusa HIPS +filament_vendor = Filatech +filament_cost = +filament_density = 1.07 +filament_spool_weight = +first_layer_temperature = 230 +first_layer_bed_temperature = 100 +temperature = 225 +bed_temperature = 110 +compatible_printers_condition = printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech HIPS @MINI] +inherits = Filatech HIPS; *ABSMINI* + +[filament:Filatech PA] +inherits = *ABSC* +filament_vendor = Filatech +filament_cost = +filament_density = 1.1 +first_layer_temperature = 275 +first_layer_bed_temperature = 110 +temperature = 275 +bed_temperature = 115 +fan_always_on = 0 +cooling = 0 +bridge_fan_speed = 25 +filament_type = NYLON +filament_max_volumetric_speed = 8 +compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech PA @MK2] +inherits = Filatech PA +first_layer_bed_temperature = 105 +bed_temperature = 110 +compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech PA @MINI] +inherits = Filatech PA +first_layer_bed_temperature = 100 +bed_temperature = 100 +compatible_printers_condition = printer_model=="MINI" + +[filament:Filatech PC] +inherits = Filatech PA +first_layer_bed_temperature = 110 +bed_temperature = 115 +filament_density = 1.2 +filament_type = PC + +[filament:Filatech PC @MK2] +inherits = Filatech PC +first_layer_bed_temperature = 105 +bed_temperature = 110 +compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech PC-ABS] +inherits = Filatech PC +first_layer_temperature = 270 +temperature = 270 +first_layer_bed_temperature = 110 +bed_temperature = 115 +filament_density = 1.08 +filament_type = PC +fan_always_on = 0 +cooling = 1 +extrusion_multiplier = 0.95 +disable_fan_first_layers = 6 + +[filament:Filatech PC-ABS @MK2] +inherits = Filatech PC-ABS +first_layer_bed_temperature = 105 +bed_temperature = 110 +compatible_printers_condition = printer_notes=~/.*PRINTER_MODEL_MK(2|2.5).*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Filatech PETG] +inherits = *PET* +filament_vendor = Filatech +filament_cost = +filament_density = 1.27 +first_layer_temperature = 240 +first_layer_bed_temperature = 75 +temperature = 245 +bed_temperature = 80 +extrusion_multiplier = 0.95 +fan_always_on = 0 + +[filament:Filatech PETG @MINI] +inherits = Filatech PETG; *PETMINI* + +[filament:Filatech Wood-PLA] +inherits = Filatech PLA +filament_cost = +filament_density = 1.05 +first_layer_temperature = 210 +compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) + +[filament:Ultrafuse PET] +inherits = *PET* +filament_vendor = BASF +filament_cost = +filament_density = 1.33 +first_layer_temperature = 220 +first_layer_bed_temperature = 70 +temperature = 215 +bed_temperature = 70 +fan_below_layer_time = 10 +min_fan_speed = 75 +max_fan_speed = 100 +bridge_fan_speed = 100 +filament_type = PET +disable_fan_first_layers = 1 +full_fan_speed_layer = 3 +filament_retract_lift = 0 +# filament_retract_length = 3 +# filament_max_volumetric_speed = 7 + +[filament:Ultrafuse PET @MINI] +inherits = Ultrafuse PET; *PETMINI* + +[filament:Ultrafuse PRO1] +inherits = Prusament PLA +filament_vendor = BASF +filament_cost = +filament_density = 1.25 +filament_spool_weight = 0 +filament_colour = #FFFFFF +# filament overrides +# filament_retract_length = 2 +# filament_retract_speed = 40 +# filament_retract_before_travel = 2 +# filament_wipe = 0 + +[filament:Ultrafuse ABS] +inherits = *ABSC* +filament_vendor = BASF +filament_cost = +filament_density = 1.04 +min_fan_speed = 10 +max_fan_speed = 20 +bed_temperature = 100 +disable_fan_first_layers = 3 +filament_colour = #FFFFFF +# filament overrides +# filament_retract_length = 2 +# filament_retract_speed = 40 +# filament_retract_before_travel = 2 +# filament_wipe = 0 + +[filament:Ultrafuse ABS @MINI] +inherits = Ultrafuse ABS; *ABSMINI* + +[filament:Ultrafuse 17-4 PH] +inherits = *ABSC* +filament_vendor = BASF +filament_cost = +filament_density = 4.5 +extrusion_multiplier = 1.08 +first_layer_temperature = 250 +first_layer_bed_temperature = 100 +temperature = 250 +bed_temperature = 100 +min_fan_speed = 0 +max_fan_speed = 0 +bridge_fan_speed = 0 +cooling = 0 +fan_always_on = 0 +filament_max_volumetric_speed = 4 +filament_type = METAL +compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) +start_filament_gcode = "M900 K0" +filament_colour = #FFFFFF + [filament:Polymaker PC-Max] inherits = *ABS* filament_vendor = Polymaker @@ -5712,6 +5981,83 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Made for Prusa +[sla_material:Prusa Vibrant Orange Tough @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 4 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Made for Prusa + +[sla_material:Prusa Deep Blue Transparent Tough @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 4 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Made for Prusa + +[sla_material:Prusa Green Dental Casting @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 3 +initial_exposure_time = 50 +material_type = Casting +material_vendor = Made for Prusa + +[sla_material:PrimaCreator Tough Light Grey @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Tough Clear @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Tough White @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Flex Clear @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Water Washable Transparent @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:DruckWege Type D Dental Model @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.2 +initial_exposure_time = 15 +material_type = Dental +material_vendor = DruckWege + +[sla_material:DruckWege Type D Standard White @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.6 +initial_exposure_time = 15 +material_type = Tough +material_vendor = DruckWege + +[sla_material:DruckWege Type D Standard Pigmentfrei Clear @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 15 +material_type = Tough +material_vendor = DruckWege + [sla_material:3DM-ABS Orange @0.025 SL1S] inherits = *0.025_sl1s* exposure_time = 1.8 @@ -5851,6 +6197,83 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Made for Prusa +[sla_material:Prusa Vibrant Orange Tough @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 5 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Made for Prusa + +[sla_material:Prusa Deep Blue Transparent Tough @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 5 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Made for Prusa + +[sla_material:Prusa Green Dental Casting @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 4 +initial_exposure_time = 50 +material_type = Casting +material_vendor = Made for Prusa + +[sla_material:PrimaCreator Tough Light Grey @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2.4 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Tough Clear @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Tough White @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Flex Clear @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Water Washable Transparent @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:DruckWege Type D Dental Model @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 1.4 +initial_exposure_time = 15 +material_type = Dental +material_vendor = DruckWege + +[sla_material:DruckWege Type D Standard White @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 15 +material_type = Tough +material_vendor = DruckWege + +[sla_material:DruckWege Type D Standard Pigmentfrei Clear @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 15 +material_type = Tough +material_vendor = DruckWege + [sla_material:3DM-ABS Orange @0.05 SL1S] inherits = *0.05_sl1s* exposure_time = 2.6 @@ -5990,6 +6413,69 @@ initial_exposure_time = 25 material_type = Tough material_vendor = Made for Prusa +[sla_material:Prusa Vibrant Orange Tough @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 10 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Made for Prusa + +[sla_material:Prusa Deep Blue Transparent Tough @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 10 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Made for Prusa + +[sla_material:Prusa Green Dental Casting @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 8 +initial_exposure_time = 50 +material_type = Casting +material_vendor = Made for Prusa + +[sla_material:PrimaCreator Tough Light Grey @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 3 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Tough Clear @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Tough White @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Flex Clear @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = PrimaCreator + +[sla_material:PrimaCreator Water Washable Transparent @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Tough +material_vendor = PrimaCreator + +[sla_material:DruckWege Type D Dental Model @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 15 +material_type = Dental +material_vendor = DruckWege + [sla_material:3DM-ABS Orange @0.1 SL1S] inherits = *0.1_sl1s* exposure_time = 3 From be236eda85748ac36dea51ac9d1b3a8af3760a8a Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sat, 23 Oct 2021 14:29:42 +0200 Subject: [PATCH 47/56] Renamed Geometry::intersect() to Geometry::convex_polygons_intersect() to convey its true meaning. --- src/libslic3r/Geometry.cpp | 2 +- src/libslic3r/Geometry.hpp | 2 +- tests/libslic3r/test_geometry.cpp | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 4cf60dbe2f..dfc463dde5 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1671,7 +1671,7 @@ void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn) } // namespace rotcalip -bool intersects(const Polygon &A, const Polygon &B) +bool convex_polygons_intersect(const Polygon &A, const Polygon &B) { using namespace rotcalip; diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 49886d4b87..d98cab9c7b 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -563,7 +563,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation) // Returns true if the intersection of the two convex polygons A and B // is not an empty set. -bool intersects(const Polygon &A, const Polygon &B); +bool convex_polygons_intersect(const Polygon &A, const Polygon &B); } } // namespace Slicer::Geometry diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index b183de1b4e..dcb1004bfa 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -468,7 +468,7 @@ TEST_CASE("Convex polygon intersection on two disjoint squares", "[Geometry][Rot Polygon B = A; B.translate(20 / SCALING_FACTOR, 0); - bool is_inters = Geometry::intersects(A, B); + bool is_inters = Geometry::convex_polygons_intersect(A, B); REQUIRE(is_inters == false); } @@ -480,7 +480,7 @@ TEST_CASE("Convex polygon intersection on two intersecting squares", "[Geometry] Polygon B = A; B.translate(5 / SCALING_FACTOR, 5 / SCALING_FACTOR); - bool is_inters = Geometry::intersects(A, B); + bool is_inters = Geometry::convex_polygons_intersect(A, B); REQUIRE(is_inters == true); } @@ -492,7 +492,7 @@ TEST_CASE("Convex polygon intersection on two squares touching one edge", "[Geom Polygon B = A; B.translate(10 / SCALING_FACTOR, 0); - bool is_inters = Geometry::intersects(A, B); + bool is_inters = Geometry::convex_polygons_intersect(A, B); REQUIRE(is_inters == false); } @@ -509,7 +509,7 @@ TEST_CASE("Convex polygon intersection on two squares touching one vertex", "[Ge svg.draw(B, "green"); svg.Close(); - bool is_inters = Geometry::intersects(A, B); + bool is_inters = Geometry::convex_polygons_intersect(A, B); REQUIRE(is_inters == false); } @@ -520,7 +520,7 @@ TEST_CASE("Convex polygon intersection on two overlapping squares", "[Geometry][ Polygon B = A; - bool is_inters = Geometry::intersects(A, B); + bool is_inters = Geometry::convex_polygons_intersect(A, B); REQUIRE(is_inters == true); } @@ -560,7 +560,7 @@ TEST_CASE("Convex polygon intersection on two overlapping squares", "[Geometry][ // bench.start(); // for (const auto &test : tests) -// results.emplace_back(Geometry::intersects(test.first, test.second)); +// results.emplace_back(Geometry::convex_polygons_intersect(test.first, test.second)); // bench.stop(); // std::cout << "Test time: " << bench.getElapsedSec() << std::endl; @@ -611,7 +611,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) { Polygon P = PRINTER_PART_POLYGONS[i]; P = Geometry::convex_hull(P.points); - bool res = Geometry::intersects(P, P); + bool res = Geometry::convex_polygons_intersect(P, P); if (!res) { SVG svg{std::string("fail_self") + std::to_string(i) + ".svg"}; svg.draw(P, "green"); @@ -644,7 +644,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali B.translate(bba.size() + bbb.size()); - bool res = Geometry::intersects(A, B); + bool res = Geometry::convex_polygons_intersect(A, B); bool ref = !intersection(A, B).empty(); if (res != ref) { @@ -669,7 +669,7 @@ TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcali A.translate(-bba.center()); B.translate(-bbb.center()); - bool res = Geometry::intersects(A, B); + bool res = Geometry::convex_polygons_intersect(A, B); bool ref = !intersection(A, B).empty(); if (res != ref) { From 36baae05f1ad9de26c5e2b5bfe270dfa5f81662b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sat, 23 Oct 2021 14:30:28 +0200 Subject: [PATCH 48/56] Commented out unused and expensive printbed_collision_state() --- src/libslic3r/Model.cpp | 4 +++- src/libslic3r/Model.hpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index b36618dd12..613e2b262e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -29,7 +29,7 @@ namespace Slic3r { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z) { - if (!Geometry::intersects(printbed_shape, obj_hull_2d)) + if (!Geometry::convex_polygons_intersect(printbed_shape, obj_hull_2d)) return ModelInstancePVS_Fully_Outside; bool contained_xy = true; @@ -43,6 +43,7 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_ return (contained_xy && contained_z) ? ModelInstancePVS_Inside : ModelInstancePVS_Partly_Outside; } +/* ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box) { const Polygon box_hull_2d({ @@ -53,6 +54,7 @@ ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_ }); return printbed_collision_state(printbed_shape, print_volume_height, box_hull_2d, box.min.z(), box.max.z()); } +*/ #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS Model& Model::assign_copy(const Model &rhs) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0a6a73cbca..2a0c83c0d9 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -914,7 +914,7 @@ enum ModelInstanceEPrintVolumeState : unsigned char ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z); // return the state of the given box // with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height) -ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box); +//ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // A single instance of a ModelObject. From 0bc2448e22a21a160f9c1f00caaa501af96194f7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sat, 23 Oct 2021 15:29:00 +0200 Subject: [PATCH 49/56] Documented the new print bed collision detection algorithm requirements: Only convex print bed is supported. Optimization of collision detection by precomputing the print bed shape type. --- src/libslic3r/Model.cpp | 3 +++ src/libslic3r/Model.hpp | 3 +++ src/slic3r/GUI/3DScene.cpp | 24 +++++++++++++++--------- src/slic3r/GUI/Plater.cpp | 4 ++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 613e2b262e..2dcf342116 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -27,6 +27,7 @@ namespace Slic3r { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +// Using rotating callipers to check for collision of two convex polygons. Thus both printbed_shape and obj_hull_2d are convex polygons. ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z) { if (!Geometry::convex_polygons_intersect(printbed_shape, obj_hull_2d)) @@ -362,6 +363,7 @@ BoundingBoxf3 Model::bounding_box() const } #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +// printbed_shape is convex polygon unsigned int Model::update_print_volume_state(const Polygon& printbed_shape, double print_volume_height) { unsigned int num_printable = 0; @@ -1571,6 +1573,7 @@ double ModelObject::get_instance_max_z(size_t instance_idx) const } #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS +// printbed_shape is convex polygon unsigned int ModelObject::check_instances_print_volume_state(const Polygon& printbed_shape, double print_volume_height) { unsigned int num_printable = 0; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2a0c83c0d9..e47ebda39a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -911,9 +911,11 @@ enum ModelInstanceEPrintVolumeState : unsigned char #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS // return the state of the given object's volume (extrusion along z of obj_hull_2d from obj_min_z to obj_max_z) // with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height) +// Using rotating callipers to check for collision of two convex polygons. ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const Polygon& obj_hull_2d, double obj_min_z, double obj_max_z); // return the state of the given box // with respect to the given print volume (extrusion along z of printbed_shape from zero to print_volume_height) +// Commented out, using rotating callipers is quite expensive for a bounding box test. //ModelInstanceEPrintVolumeState printbed_collision_state(const Polygon& printbed_shape, double print_volume_height, const BoundingBoxf3& box); #endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS @@ -1122,6 +1124,7 @@ public: // Set the print_volume_state of PrintObject::instances, // return total number of printable objects. #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS + // printbed_shape is convex polygon unsigned int update_print_volume_state(const Polygon& printbed_shape, double print_volume_height); #else unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 3dd0c0e1fe..673a9a9241 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -992,6 +992,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M auto check_against_circular_bed = [](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) { const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); + //FIXME 2D convex hull is O(n log n), while testing the 2D points against 2D circle is O(n). const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast(), 0.0f); size_t outside_count = 0; const double sq_radius = sqr(radius); @@ -1013,6 +1014,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast(), 0.0f); const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); + // Using rotating callipers to check for collision of two convex polygons. ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z()); bool contained = (volume_state == ModelInstancePVS_Inside); bool intersects = (volume_state == ModelInstancePVS_Partly_Outside); @@ -1041,6 +1043,14 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside; bool contained_min_one = false; + enum class BedShape { Rectangle, Circle, Convex, NonConvex }; + Vec2d center; + double radius; + BedShape bed_shape = + GUI::Bed3D::is_rectangle(opt->values) ? BedShape::Rectangle : + GUI::Bed3D::is_circle(opt->values, ¢er, &radius) ? BedShape::Circle : + GUI::Bed3D::is_convex(opt->values) ? BedShape::Convex : BedShape::NonConvex; + for (GLVolume* volume : this->volumes) { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS if (as_toolpaths && !volume->is_extrusion_path) @@ -1048,15 +1058,11 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M else if (!as_toolpaths && (volume->is_modifier || (!volume->shader_outside_printer_detection_enabled && (volume->is_wipe_tower || volume->composite_id.volume_id < 0)))) continue; - if (GUI::Bed3D::is_rectangle(opt->values)) - check_against_rectangular_bed(*volume, overall_state); - else { - Vec2d center; - double radius; - if (GUI::Bed3D::is_circle(opt->values, ¢er, &radius)) - check_against_circular_bed(*volume, overall_state, center, radius); - else if (GUI::Bed3D::is_convex(opt->values)) - check_against_convex_bed(*volume, overall_state); + switch (bed_shape) { + case BedShape::Rectangle: check_against_rectangular_bed(*volume, overall_state); break; + case BedShape::Circle: check_against_circular_bed(*volume, overall_state, center, radius); break; + case BedShape::Convex: check_against_convex_bed(*volume, overall_state); break; + default: break; } contained_min_one |= !volume->is_outside; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c6de7df228..afbfc7d3c8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3011,9 +3011,9 @@ void Plater::priv::update_print_volume_state() { #if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS const ConfigOptionPoints* opt = dynamic_cast(this->config->option("bed_shape")); - const Polygon bed_poly = offset(Polygon::new_scale(opt->values), static_cast(scale_(BedEpsilon))).front(); + const Polygon bed_poly_convex = offset(Geometry::convex_hull(Polygon::new_scale(opt->values).points), static_cast(scale_(BedEpsilon))).front(); const float bed_height = this->config->opt_float("max_print_height"); - this->q->model().update_print_volume_state(bed_poly, bed_height); + this->q->model().update_print_volume_state(bed_poly_convex, bed_height); #else BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt("bed_shape")->values)); BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height")))); From ae628012507aa382f1329f60a898aaa9ba46c21b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sat, 23 Oct 2021 20:13:25 +0200 Subject: [PATCH 50/56] Implemented "Details" section enclosing OpenGL extensions when copying sysinfo to clipboard for inserting into github issue. Fix of [Feature Request] Help => System Info => Copy to Clipboard: wrap list of extensions with
tag #6830 --- src/slic3r/GUI/GUI_App.cpp | 6 ++++-- src/slic3r/GUI/GUI_App.hpp | 4 +++- src/slic3r/GUI/OpenGLManager.cpp | 23 ++++++++++++++++------- src/slic3r/GUI/OpenGLManager.hpp | 4 +++- src/slic3r/GUI/SysInfoDialog.cpp | 4 ++-- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b6b3ca91b6..787382c239 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -721,9 +721,11 @@ GUI_App::~GUI_App() delete preset_updater; } -std::string GUI_App::get_gl_info(bool format_as_html, bool extensions) +// If formatted for github, plaintext with OpenGL extensions enclosed into
. +// Otherwise HTML formatted for the system info dialog. +std::string GUI_App::get_gl_info(bool for_github) { - return OpenGLManager::get_gl_info().to_string(format_as_html, extensions); + return OpenGLManager::get_gl_info().to_string(for_github); } wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas) diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index d350da9693..ad42803f6b 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -172,7 +172,9 @@ public: // Process command line parameters cached in this->init_params, // load configs, STLs etc. void post_init(); - static std::string get_gl_info(bool format_as_html, bool extensions); + // If formatted for github, plaintext with OpenGL extensions enclosed into
. + // Otherwise HTML formatted for the system info dialog. + static std::string get_gl_info(bool for_github); wxGLContext* init_glcontext(wxGLCanvas& canvas); bool init_opengl(); diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index 3baa8a4dab..6616cc20d7 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -157,13 +157,16 @@ bool OpenGLManager::GLInfo::is_glsl_version_greater_or_equal_to(unsigned int maj return version_greater_or_equal_to(m_glsl_version, major, minor); } -std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const +// If formatted for github, plaintext with OpenGL extensions enclosed into
. +// Otherwise HTML formatted for the system info dialog. +std::string OpenGLManager::GLInfo::to_string(bool for_github) const { if (!m_detected) detect(); std::stringstream out; + const bool format_as_html = ! for_github; std::string h2_start = format_as_html ? "" : ""; std::string h2_end = format_as_html ? "" : ""; std::string b_start = format_as_html ? "" : ""; @@ -176,18 +179,24 @@ std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extension out << b_start << "Renderer: " << b_end << m_renderer << line_end; out << b_start << "GLSL version: " << b_end << m_glsl_version << line_end; - if (extensions) { + { std::vector extensions_list; std::string extensions_str = gl_get_string_safe(GL_EXTENSIONS, ""); - boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); + boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_on); if (!extensions_list.empty()) { - out << h2_start << "Installed extensions:" << h2_end << line_end; + if (for_github) + out << "
\nInstalled extensions:\n"; + else + out << h2_start << "Installed extensions:" << h2_end << line_end; std::sort(extensions_list.begin(), extensions_list.end()); - for (const std::string& ext : extensions_list) { - out << ext << line_end; - } + for (const std::string& ext : extensions_list) + if (! ext.empty()) + out << ext << line_end; + + if (for_github) + out << "
\n"; } } diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index 716256e406..72a4e6bc07 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -46,7 +46,9 @@ public: bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; - std::string to_string(bool format_as_html, bool extensions) const; + // If formatted for github, plaintext with OpenGL extensions enclosed into
. + // Otherwise HTML formatted for the system info dialog. + std::string to_string(bool for_github) const; private: void detect() const; diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index 5475a36eaa..1a8542438c 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -158,7 +158,7 @@ SysInfoDialog::SysInfoDialog() "" "", bgr_clr_str, text_clr_str, text_clr_str, blacklisted_libraries_message, - get_mem_info(true), wxGetApp().get_gl_info(true, true), + get_mem_info(true), wxGetApp().get_gl_info(false), "" + _L("Eigen vectorization supported:") + " " + Eigen::SimdInstructionSetsInUse()); m_opengl_info_html->SetPage(text); @@ -215,7 +215,7 @@ void SysInfoDialog::on_dpi_changed(const wxRect &suggested_rect) void SysInfoDialog::onCopyToClipboard(wxEvent &) { wxTheClipboard->Open(); - const auto text = get_main_info(false) + "\n" + wxGetApp().get_gl_info(false, true); + const auto text = get_main_info(false) + "\n" + wxGetApp().get_gl_info(true); wxTheClipboard->SetData(new wxTextDataObject(text)); wxTheClipboard->Close(); } From 9651ff19ef436600cbc8dd0f787940f8f5d0b22c Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sun, 24 Oct 2021 11:12:15 +0200 Subject: [PATCH 51/56] Fix of [prusa3d/PrusaSlicer] Typo in Keyboard Shortcuts (Issue #7161) --- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index d16161f890..c330f7cc92 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -193,7 +193,7 @@ void KBShortcutsDialog::fill_shortcuts() m_full_shortcuts.push_back({ { _L("Gizmos"), _L("The following shortcuts are applicable when the specified gizmo is active") }, gizmos_shortcuts }); Shortcuts object_list_shortcuts = { - { "P", L("Set selected items as Ptrintable/Unprintable") }, + { "P", L("Set selected items as Printable/Unprintable") }, { "0", L("Set default extruder for the selected items") }, { "1-9", L("Set extruder number for the selected items") }, }; From 0f9a9de745726ef9f67a5b7ebfea3cc33e432274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 25 Oct 2021 08:41:41 +0200 Subject: [PATCH 52/56] Fixed darker colors of objects inside multi-material gizmo when objects are mirrored. --- resources/shaders/mm_gouraud.fs | 5 +++++ src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/resources/shaders/mm_gouraud.fs b/resources/shaders/mm_gouraud.fs index f7154b419a..5932efe592 100644 --- a/resources/shaders/mm_gouraud.fs +++ b/resources/shaders/mm_gouraud.fs @@ -22,6 +22,8 @@ uniform vec4 uniform_color; varying vec3 clipping_planes_dots; varying vec4 model_pos; +uniform bool volume_mirrored; + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -34,6 +36,9 @@ void main() triangle_normal = -triangle_normal; #endif + if (volume_mirrored) + triangle_normal = -triangle_normal; + // First transform the normal into camera space and normalize the result. vec3 eye_normal = normalize(gl_NormalMatrix * triangle_normal); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index ee15dab888..4e86b562b1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -203,6 +203,7 @@ void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const glsafe(::glMultMatrixd(trafo_matrix.data())); shader->set_uniform("volume_world_matrix", trafo_matrix); + shader->set_uniform("volume_mirrored", is_left_handed); m_triangle_selectors[mesh_id]->render(m_imgui); glsafe(::glPopMatrix()); From 10f24119d0e66ce8336c961b1a4ae440957670a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 25 Oct 2021 08:43:30 +0200 Subject: [PATCH 53/56] Fixed the propagation of painted top and bottom layers for mirrored objects in multi-material segmentation. --- src/libslic3r/TriangleMeshSlicer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index 0f3d0f5b64..83a2be3226 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -1919,6 +1919,7 @@ void slice_mesh_slabs( #endif // EXPENSIVE_DEBUG_CHECKS std::vector vertices_transformed = transform_mesh_vertices_for_slicing(mesh, trafo); + const bool mirrored = trafo.matrix().determinant() < 0; std::vector face_orientation(mesh.indices.size(), FaceOrientation::Up); for (const stl_triangle_vertex_indices &tri : mesh.indices) { @@ -1929,7 +1930,7 @@ void slice_mesh_slabs( const Point a = to_2d(fa).cast(); const Point b = to_2d(fb).cast(); const Point c = to_2d(fc).cast(); - const int64_t d = cross2((b - a).cast(), (c - b).cast()); + const int64_t d = cross2((b - a).cast(), (c - b).cast()) * (mirrored ? -1 : 1); FaceOrientation fo = FaceOrientation::Vertical; if (d > 0) fo = FaceOrientation::Up; From dcbb3bef451c0e65553e4a035472c7ce40542f97 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 25 Oct 2021 09:57:51 +0200 Subject: [PATCH 54/56] Fixed crash in bed collision detection when there are sinking toolpaths --- src/slic3r/GUI/3DScene.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 673a9a9241..c8e72ba399 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -980,7 +980,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M { unscale(bed_box_2D.max.x()), unscale(bed_box_2D.max.y()), bed_height }); auto check_against_rectangular_bed = [&print_volume](GLVolume& volume, ModelInstanceEPrintVolumeState& state) { - const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); + const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); volume.is_outside = !print_volume.contains(*bb); if (volume.printable) { if (state == ModelInstancePVS_Inside && volume.is_outside) @@ -991,7 +991,7 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M }; auto check_against_circular_bed = [](GLVolume& volume, ModelInstanceEPrintVolumeState& state, const Vec2d& center, double radius) { - const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); + const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1)? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); //FIXME 2D convex hull is O(n log n), while testing the 2D points against 2D circle is O(n). const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast(), 0.0f); size_t outside_count = 0; @@ -1011,9 +1011,9 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M }; auto check_against_convex_bed = [&bed_poly, bed_height](GLVolume& volume, ModelInstanceEPrintVolumeState& state) { - const TriangleMesh* mesh = volume.is_sinking() ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); + const TriangleMesh* mesh = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &GUI::wxGetApp().plater()->model().objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : volume.convex_hull(); const Polygon volume_hull_2d = its_convex_hull_2d_above(mesh->its, volume.world_matrix().cast(), 0.0f); - const BoundingBoxf3* const bb = volume.is_sinking() ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); + const BoundingBoxf3* const bb = (volume.is_sinking() && volume.object_idx() != -1 && volume.volume_idx() != -1) ? &volume.transformed_non_sinking_bounding_box() : &volume.transformed_convex_hull_bounding_box(); // Using rotating callipers to check for collision of two convex polygons. ModelInstanceEPrintVolumeState volume_state = printbed_collision_state(bed_poly, bed_height, volume_hull_2d, bb->min.z(), bb->max.z()); bool contained = (volume_state == ModelInstancePVS_Inside); From 743b99d22c895f9d22911e5dc403c027d0526b74 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 25 Oct 2021 12:03:35 +0200 Subject: [PATCH 55/56] Unit tests to the new optimized implementation of Line::parallel_to() a Line::perpendicular_to(). --- tests/libslic3r/test_geometry.cpp | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index dcb1004bfa..8261fe249d 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -19,6 +19,66 @@ using namespace Slic3r; +TEST_CASE("Line::parallel_to", "[Geometry]"){ + Line l{ { 100000, 0 }, { 0, 0 } }; + Line l2{ { 200000, 0 }, { 0, 0 } }; + REQUIRE(l.parallel_to(l)); + REQUIRE(l.parallel_to(l2)); + + Line l3(l2); + l3.rotate(0.9 * EPSILON, { 0, 0 }); + REQUIRE(l.parallel_to(l3)); + + Line l4(l2); + l4.rotate(1.1 * EPSILON, { 0, 0 }); + REQUIRE(! l.parallel_to(l4)); + + // The angle epsilon is so low that vectors shorter than 100um rotated by epsilon radians are not rotated at all. + Line l5{ { 20000, 0 }, { 0, 0 } }; + l5.rotate(1.1 * EPSILON, { 0, 0 }); + REQUIRE(l.parallel_to(l5)); + + l.rotate(1., { 0, 0 }); + Point offset{ 342876, 97636249 }; + l.translate(offset); + l3.rotate(1., { 0, 0 }); + l3.translate(offset); + l4.rotate(1., { 0, 0 }); + l4.translate(offset); + REQUIRE(l.parallel_to(l3)); + REQUIRE(!l.parallel_to(l4)); +} + +TEST_CASE("Line::perpendicular_to", "[Geometry]") { + Line l{ { 100000, 0 }, { 0, 0 } }; + Line l2{ { 0, 200000 }, { 0, 0 } }; + REQUIRE(! l.perpendicular_to(l)); + REQUIRE(l.perpendicular_to(l2)); + + Line l3(l2); + l3.rotate(0.9 * EPSILON, { 0, 0 }); + REQUIRE(l.perpendicular_to(l3)); + + Line l4(l2); + l4.rotate(1.1 * EPSILON, { 0, 0 }); + REQUIRE(! l.perpendicular_to(l4)); + + // The angle epsilon is so low that vectors shorter than 100um rotated by epsilon radians are not rotated at all. + Line l5{ { 0, 20000 }, { 0, 0 } }; + l5.rotate(1.1 * EPSILON, { 0, 0 }); + REQUIRE(l.perpendicular_to(l5)); + + l.rotate(1., { 0, 0 }); + Point offset{ 342876, 97636249 }; + l.translate(offset); + l3.rotate(1., { 0, 0 }); + l3.translate(offset); + l4.rotate(1., { 0, 0 }); + l4.translate(offset); + REQUIRE(l.perpendicular_to(l3)); + REQUIRE(! l.perpendicular_to(l4)); +} + TEST_CASE("Polygon::contains works properly", "[Geometry]"){ // this test was failing on Windows (GH #1950) Slic3r::Polygon polygon(std::vector({ From 19387186a5dc4b5a69fc1a0f5cf11b876ef5fd94 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 25 Oct 2021 12:31:19 +0200 Subject: [PATCH 56/56] #7110 - Fixed calculation of estimated times for layers --- src/libslic3r/GCode/GCodeProcessor.cpp | 25 ++++++++++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 2 +- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6121fb7fdc..e146fb5178 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -205,9 +205,7 @@ void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time) if (!enabled) return; - time += additional_time; - gcode_time.cache += additional_time; - calculate_time(); + calculate_time(0, additional_time); } static void planner_forward_pass_kernel(GCodeProcessor::TimeBlock& prev, GCodeProcessor::TimeBlock& curr) @@ -280,7 +278,7 @@ static void recalculate_trapezoids(std::vector& block } } -void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) +void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, float additional_time) { if (!enabled || blocks.size() < 2) return; @@ -302,20 +300,21 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks) for (size_t i = 0; i < n_blocks_process; ++i) { const TimeBlock& block = blocks[i]; float block_time = block.time(); + if (i == 0) + block_time += additional_time; + time += block_time; gcode_time.cache += block_time; moves_time[static_cast(block.move_type)] += block_time; roles_time[static_cast(block.role)] += block_time; - if (block.layer_id > 0) { - if (block.layer_id >= layers_time.size()) { - size_t curr_size = layers_time.size(); - layers_time.resize(block.layer_id); - for (size_t i = curr_size; i < layers_time.size(); ++i) { - layers_time[i] = 0.0f; - } + if (block.layer_id >= layers_time.size()) { + const size_t curr_size = layers_time.size(); + layers_time.resize(block.layer_id); + for (size_t i = curr_size; i < layers_time.size(); ++i) { + layers_time[i] = 0.0f; } - layers_time[block.layer_id - 1] += block_time; } + layers_time[block.layer_id - 1] += block_time; g1_times_cache.push_back({ block.g1_line_id, time }); // update times for remaining time to printer stop placeholders auto it_stop_time = std::lower_bound(stop_times.begin(), stop_times.end(), block.g1_line_id, @@ -2542,7 +2541,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) block.role = m_extrusion_role; block.distance = distance; block.g1_line_id = m_g1_line_id; - block.layer_id = m_layer_id; + block.layer_id = std::max(1, m_layer_id); // calculates block cruise feedrate float min_feedrate_factor = 1.0f; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index e7d6021554..55ae692bc7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -271,7 +271,7 @@ namespace Slic3r { // Simulates firmware st_synchronize() call void simulate_st_synchronize(float additional_time = 0.0f); - void calculate_time(size_t keep_last_n_blocks = 0); + void calculate_time(size_t keep_last_n_blocks = 0, float additional_time = 0.0f); }; struct TimeProcessor