From 22dd20ab587e12c633e65958833f466b32d0723f Mon Sep 17 00:00:00 2001 From: "liz.li" Date: Fri, 28 Oct 2022 11:19:43 +0800 Subject: [PATCH] NEW:add variable layer height Change-Id: Idef7f0dea32e4faaeb4d6eb188695cc7b554099c (cherry picked from commit 4969835159eebb356e9b26dab223ad0a18882b70) --- .../images/toolbar_variable_layer_height.svg | 7 + src/libslic3r/PrintObject.cpp | 25 +- src/libslic3r/Slicing.cpp | 47 +- src/slic3r/GUI/GLCanvas3D.cpp | 700 +++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 120 ++- src/slic3r/GUI/GLToolbar.cpp | 2 +- src/slic3r/GUI/GLToolbar.hpp | 1 + src/slic3r/GUI/GUI_Preview.cpp | 16 + src/slic3r/GUI/GUI_Preview.hpp | 4 + src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 3 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 6 + src/slic3r/GUI/NotificationManager.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 62 +- src/slic3r/GUI/Plater.hpp | 1 + 16 files changed, 927 insertions(+), 75 deletions(-) create mode 100644 resources/images/toolbar_variable_layer_height.svg diff --git a/resources/images/toolbar_variable_layer_height.svg b/resources/images/toolbar_variable_layer_height.svg new file mode 100644 index 000000000..564684122 --- /dev/null +++ b/resources/images/toolbar_variable_layer_height.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index cc5dc6a45..4d936191b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1892,9 +1892,8 @@ std::vector PrintObject::object_extruders() const bool PrintObject::update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector &layer_height_profile) { bool updated = false; - //BBS:annotate these part and will do adaptive layer height below - /*if (layer_height_profile.empty()) { + if (layer_height_profile.empty()) { // use the constructor because the assignement is crashing on ASAN OsX layer_height_profile = std::vector(model_object.layer_height_profile.get()); // layer_height_profile = model_object.layer_height_profile; @@ -1909,22 +1908,22 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max + slicing_parameters.object_print_z_min) > 1e-3)) layer_height_profile.clear(); - if (layer_height_profile.empty()) { + if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.layer_height) { //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); updated = true; - }*/ + } //BBS - if (slicing_parameters.adaptive_layer_height) { - layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object, 0.5); - HeightProfileSmoothingParams smoothing_params(5, true); - layer_height_profile = smooth_height_profile(layer_height_profile, slicing_parameters, smoothing_params); - } - else { - layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); - } - updated = true; + //if (slicing_parameters.adaptive_layer_height) { + // layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object, 0.5); + // HeightProfileSmoothingParams smoothing_params(5, true); + // layer_height_profile = smooth_height_profile(layer_height_profile, slicing_parameters, smoothing_params); + //} + //else { + // layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); + //} + //updated = true; return updated; } diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 7747b4676..1206c24b0 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -400,31 +400,32 @@ std::vector smooth_height_profile(const std::vector& profile, co }; //BBS: avoid the layer height change to be too steep - auto has_steep_height_change = [&slicing_params](const std::vector& profile, const double height_step) { - //BBS: skip first layer - size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0; - size_t size = profile.size(); - //BBS: not enough data to smmoth, return false directly - if ((int)size - (int)skip_count < 6) - return false; + //auto has_steep_height_change = [&slicing_params](const std::vector& profile, const double height_step) { + // //BBS: skip first layer + // size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0; + // size_t size = profile.size(); + // //BBS: not enough data to smmoth, return false directly + // if ((int)size - (int)skip_count < 6) + // return false; - //BBS: Don't need to check the difference between top layer and the last 2th layer - for (size_t i = skip_count; i < size - 6; i += 2) { - if (abs(profile[i + 1] - profile[i + 3]) > height_step) - return true; - } - return false; - }; + // //BBS: Don't need to check the difference between top layer and the last 2th layer + // for (size_t i = skip_count; i < size - 6; i += 2) { + // if (abs(profile[i + 1] - profile[i + 3]) > height_step) + // return true; + // } + // return false; + //}; - int count = 0; - std::vector ret = profile; - bool has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP); - while (has_steep_change && count < 6) { - ret = gauss_blur(ret, smoothing_params); - has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP); - count++; - } - return ret; + //int count = 0; + //std::vector ret = profile; + //bool has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP); + //while (has_steep_change && count < 6) { + // ret = gauss_blur(ret, smoothing_params); + // has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP); + // count++; + //} + //return ret; + return gauss_blur(profile, smoothing_params); } void adjust_layer_height_profile( diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cc869b385..ca33805e7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -119,6 +119,488 @@ float RetinaHelper::get_scale_factor() { return float(m_window->GetContentScaleF #undef Convex #endif +GLCanvas3D::LayersEditing::~LayersEditing() +{ + if (m_z_texture_id != 0) { + glsafe(::glDeleteTextures(1, &m_z_texture_id)); + m_z_texture_id = 0; + } + delete m_slicing_parameters; +} + +const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; + +void GLCanvas3D::LayersEditing::init() +{ + glsafe(::glGenTextures(1, (GLuint*)&m_z_texture_id)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); +} + +void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) +{ + m_config = config; + delete m_slicing_parameters; + m_slicing_parameters = nullptr; + m_layers_texture.valid = false; + m_layer_height_profile.clear(); +} + +void GLCanvas3D::LayersEditing::select_object(const Model& model, int object_id) +{ + const ModelObject* model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr; + // Maximum height of an object changes when the object gets rotated or scaled. + // Changing maximum height of an object will invalidate the layer heigth editing profile. + // m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently. + const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast(model_object_new->bounding_box().max.z()); + if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z || + (model_object_new != nullptr && m_model_object->id() != model_object_new->id())) { + m_layer_height_profile.clear(); + delete m_slicing_parameters; + m_slicing_parameters = nullptr; + m_layers_texture.valid = false; + this->last_object_id = object_id; + m_model_object = model_object_new; + m_object_max_z = new_max_z; + } +} + +bool GLCanvas3D::LayersEditing::is_allowed() const +{ + return wxGetApp().get_shader("variable_layer_height") != nullptr && m_z_texture_id > 0; +} + +bool GLCanvas3D::LayersEditing::is_enabled() const +{ + return m_enabled; +} + +void GLCanvas3D::LayersEditing::set_enabled(bool enabled) +{ + m_enabled = is_allowed() && enabled; +} + +float GLCanvas3D::LayersEditing::s_overlay_window_width; + +void GLCanvas3D::LayersEditing::show_tooltip_information(const GLCanvas3D& canvas, std::map captions_texts, float x, float y) +{ + ImTextureID normal_id = canvas.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); + ImTextureID hover_id = canvas.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + float caption_max = 0.f; + for (auto caption_text : captions_texts) { + caption_max = std::max(imgui.calc_text_size(caption_text.first).x, caption_max); + } + caption_max += GImGui->Style.WindowPadding.x + imgui.scaled(1); + + float font_size = ImGui::GetFontSize(); + ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {0.0f, GImGui->Style.FramePadding.y}); + ImGui::ImageButton3(normal_id, hover_id, button_size); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip2(ImVec2(x, y)); + auto draw_text_with_caption = [this, &caption_max, &imgui](const wxString& caption, const wxString& text) { + imgui.text_colored(ImGuiWrapper::COL_ACTIVE, caption); + ImGui::SameLine(caption_max); + imgui.text_colored(ImGuiWrapper::COL_WINDOW_BG, text); + }; + + for (const auto& caption_text : captions_texts) draw_text_with_caption(caption_text.first, caption_text.second); + + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); +} + +void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanvas3D& canvas) { + if (!m_enabled) + return; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + const Size& cnv_size = canvas.get_canvas_size(); + float zoom = (float)wxGetApp().plater()->get_camera().get_zoom(); + float left_pos = canvas.m_main_toolbar.get_item("layersediting")->render_left_pos; + const float x = 0.5 * cnv_size.get_width() + left_pos * zoom; + imgui.set_next_window_pos(x, canvas.m_main_toolbar.get_height(), ImGuiCond_Always, 0.0f, 0.0f); + + imgui.push_toolbar_style(canvas.get_scale()); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f * canvas.get_scale(), 4.0f * canvas.get_scale())); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f * canvas.get_scale(), 10.0f * canvas.get_scale())); + imgui.begin(_L("Variable layer height"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); + const float sliders_width = imgui.scaled(7.0f); + const float input_box_width = 1.5 * imgui.get_slider_icon_size().x; + + if (imgui.button(_L("Adaptive"))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_quality)); + ImGui::SameLine(); + float text_align = ImGui::GetCursorPosX(); + ImGui::AlignTextToFramePadding(); + imgui.text(_L("Quality / Speed")); + if (ImGui::IsItemHovered()) { + //ImGui::BeginTooltip(); + //ImGui::TextUnformatted(_L("Higher print quality versus higher print speed.").ToUTF8()); + //ImGui::EndTooltip(); + } + ImGui::SameLine(); + float slider_align = ImGui::GetCursorPosX(); + ImGui::PushItemWidth(sliders_width); + m_adaptive_quality = std::clamp(m_adaptive_quality, 0.0f, 1.f); + imgui.bbl_slider_float_style("##adaptive_slider", &m_adaptive_quality, 0.0f, 1.f, "%.2f"); + ImGui::SameLine(); + float input_align = ImGui::GetCursorPosX(); + ImGui::PushItemWidth(input_box_width); + ImGui::BBLDragFloat("##adaptive_input", &m_adaptive_quality, 0.05f, 0.0f, 0.0f, "%.2f"); + + if (imgui.button(_L("Smooth"))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params)); + ImGui::SameLine(); + ImGui::SetCursorPosX(text_align); + ImGui::AlignTextToFramePadding(); + imgui.text(_L("Radius")); + ImGui::SameLine(); + ImGui::SetCursorPosX(slider_align); + ImGui::PushItemWidth(sliders_width); + int radius = (int)m_smooth_params.radius; + int v_min = 1, v_max = 10; + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 0.00f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.81f, 0.81f, 0.81f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + if(ImGui::BBLSliderScalar("##radius_slider", ImGuiDataType_S32, &radius, &v_min, &v_max)){ + radius = std::clamp(radius, 1, 10); + m_smooth_params.radius = (unsigned int)radius; + } + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(); + ImGui::SameLine(); + ImGui::SetCursorPosX(input_align); + ImGui::PushItemWidth(input_box_width); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.00f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.00f, 0.68f, 0.26f, 0.00f)); + ImGui::BBLDragScalar("##radius_input", ImGuiDataType_S32, &radius, 1, &v_min, &v_max); + ImGui::PopStyleColor(3); + + imgui.bbl_checkbox("##keep_min", m_smooth_params.keep_min); + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); + imgui.text(_L("Keep min")); + + ImGui::Separator(); + + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + canvas.m_main_toolbar.get_height(); + std::map captions_texts = { + {_L("Left mouse button:") ,_L("Add detail")}, + {_L("Right mouse button:"), _L("Remove detail")}, + {_L("Shift + Left mouse button:"),_L("Reset to base")}, + {_L("Shift + Right mouse button:"), _L("Smoothing")}, + {_L("Mouse wheel:"), _L("Increase/decrease edit area")} + }; + show_tooltip_information(canvas, captions_texts, x, get_cur_y); + ImGui::SameLine(); + if (imgui.button(_L("Reset"))) + wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE)); + + GLCanvas3D::LayersEditing::s_overlay_window_width = ImGui::GetWindowSize().x; + imgui.end(); + ImGui::PopStyleVar(2); + imgui.pop_toolbar_style(); +} + +void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) +{ + render_variable_layer_height_dialog(canvas); + const Rect& bar_rect = get_bar_rect_viewport(canvas); + render_background_texture(canvas, bar_rect); + render_curve(bar_rect); +} + +float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) +{ + const Vec2d mouse_pos = canvas.get_local_mouse_position(); + const Rect& rect = get_bar_rect_screen(canvas); + float x = (float)mouse_pos.x(); + float y = (float)mouse_pos.y(); + float t = rect.get_top(); + float b = rect.get_bottom(); + + return (rect.get_left() <= x && x <= rect.get_right() && t <= y && y <= b) ? + // Inside the bar. + (b - y - 1.0f) / (b - t - 1.0f) : + // Outside the bar. + -1000.0f; +} + +bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) +{ + const Rect& rect = get_bar_rect_screen(canvas); + return rect.get_left() <= x && x <= rect.get_right() && rect.get_top() <= y && y <= rect.get_bottom(); +} + +Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) +{ + const Size& cnv_size = canvas.get_canvas_size(); + float w = (float)cnv_size.get_width(); + float h = (float)cnv_size.get_height(); + + return { w - thickness_bar_width(canvas), 0.0f, w, h }; +} + +Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) +{ + const Size& cnv_size = canvas.get_canvas_size(); + float half_w = 0.5f * (float)cnv_size.get_width(); + float half_h = 0.5f * (float)cnv_size.get_height(); + float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); + return { (half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom }; +} + +bool GLCanvas3D::LayersEditing::is_initialized() const +{ + return wxGetApp().get_shader("variable_layer_height") != nullptr; +} + +std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) const +{ + std::string ret; + if (m_enabled && m_layer_height_profile.size() >= 4) { + float z = get_cursor_z_relative(canvas); + if (z != -1000.0f) { + z *= m_object_max_z; + + float h = 0.0f; + for (size_t i = m_layer_height_profile.size() - 2; i >= 2; i -= 2) { + const float zi = static_cast(m_layer_height_profile[i]); + const float zi_1 = static_cast(m_layer_height_profile[i - 2]); + if (zi_1 <= z && z <= zi) { + float dz = zi - zi_1; + h = (dz != 0.0f) ? static_cast(lerp(m_layer_height_profile[i - 1], m_layer_height_profile[i + 1], (z - zi_1) / dz)) : + static_cast(m_layer_height_profile[i + 1]); + break; + } + } + if (h > 0.0f) + ret = std::to_string(h); + } + } + return ret; +} + +void GLCanvas3D::LayersEditing::render_background_texture(const GLCanvas3D& canvas, const Rect& bar_rect) +{ + GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); + if (shader == nullptr) + return; + + shader->start_using(); + + shader->set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z)); + shader->set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height); + shader->set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); + shader->set_uniform("z_cursor_band_width", band_width); + shader->set_uniform("object_max_z", m_object_max_z); + + glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); + + // Render the color bar + const float l = bar_rect.get_left(); + const float r = bar_rect.get_right(); + const float t = bar_rect.get_top(); + const float b = bar_rect.get_bottom(); + + ::glBegin(GL_QUADS); + ::glNormal3f(0.0f, 0.0f, 1.0f); + ::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(l, b); + ::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(r, b); + ::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(r, t); + ::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(l, t); + glsafe(::glEnd()); + + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + + shader->stop_using(); +} + +void GLCanvas3D::LayersEditing::render_curve(const Rect & bar_rect) +{ + //FIXME show some kind of legend. + if (!m_slicing_parameters) + return; + + // Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. + const float scale_x = bar_rect.get_width() / float(1.12 * m_slicing_parameters->max_layer_height); + const float scale_y = bar_rect.get_height() / m_object_max_z; + const float x = bar_rect.get_left() + float(m_slicing_parameters->layer_height) * scale_x; + + // Baseline + glsafe(::glColor3f(0.0f, 0.0f, 0.0f)); + ::glBegin(GL_LINE_STRIP); + ::glVertex2f(x, bar_rect.get_bottom()); + ::glVertex2f(x, bar_rect.get_top()); + glsafe(::glEnd()); + + // Curve + glsafe(::glColor3f(0.0f, 0.0f, 1.0f)); + ::glBegin(GL_LINE_STRIP); + for (unsigned int i = 0; i < m_layer_height_profile.size(); i += 2) + ::glVertex2f(bar_rect.get_left() + (float)m_layer_height_profile[i + 1] * scale_x, bar_rect.get_bottom() + (float)m_layer_height_profile[i] * scale_y); + glsafe(::glEnd()); +} + +void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D & canvas, const GLVolumeCollection & volumes)//render volume and layer height texture (has mapping relation with each other) +{ + assert(this->is_allowed()); + assert(this->last_object_id != -1); + + GLShaderProgram* current_shader = wxGetApp().get_current_shader(); + ScopeGuard guard([current_shader]() { if (current_shader != nullptr) current_shader->start_using(); }); + if (current_shader != nullptr) + current_shader->stop_using(); + + GLShaderProgram* shader = wxGetApp().get_shader("variable_layer_height"); + if (shader == nullptr) + return; + + shader->start_using(); + + generate_layer_height_texture(); + + // Uniforms were resolved, go ahead using the layer editing shader. + shader->set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * float(m_object_max_z))); + shader->set_uniform("z_texture_row_to_normalized", 1.0f / float(m_layers_texture.height)); + shader->set_uniform("z_cursor", float(m_object_max_z) * float(this->get_cursor_z_relative(canvas))); + shader->set_uniform("z_cursor_band_width", float(this->band_width)); + + // Initialize the layer height texture mapping. + const GLsizei w = (GLsizei)m_layers_texture.width; + const GLsizei h = (GLsizei)m_layers_texture.height; + const GLsizei half_w = w / 2; + const GLsizei half_h = h / 2; + glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data())); + glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4)); + for (GLVolume* glvolume : volumes.volumes) { + // Render the object using the layer editing shader and texture. + if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) + continue; + + shader->set_uniform("volume_world_matrix", glvolume->world_matrix()); + shader->set_uniform("object_max_z", 0.0f); + + glvolume->render(); + } + // Revert back to the previous shader. + glBindTexture(GL_TEXTURE_2D, 0); +} + +void GLCanvas3D::LayersEditing::adjust_layer_height_profile() +{ + this->update_slicing_parameters(); + PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile); + Slic3r::adjust_layer_height_profile(*m_slicing_parameters, m_layer_height_profile, this->last_z, this->strength, this->band_width, this->last_action); + m_layers_texture.valid = false; +} + +void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D & canvas) +{ + const_cast(m_model_object)->layer_height_profile.clear(); + m_layer_height_profile.clear(); + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); +} + +void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D & canvas, float quality_factor) +{ + this->update_slicing_parameters(); + m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, quality_factor); + const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); +} + +void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D & canvas, const HeightProfileSmoothingParams & smoothing_params) +{ + this->update_slicing_parameters(); + m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params); + const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); +} + +void GLCanvas3D::LayersEditing::generate_layer_height_texture() +{ + this->update_slicing_parameters(); + // Always try to update the layer height profile. + bool update = !m_layers_texture.valid; + if (PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile)) { + // Initialized to the default value. + update = true; + } + // Update if the layer height profile was changed, or when the texture is not valid. + if (!update && !m_layers_texture.data.empty() && m_layers_texture.cells > 0) + // Texture is valid, don't update. + return; + + if (m_layers_texture.data.empty()) { + m_layers_texture.width = 1024; + m_layers_texture.height = 1024; + m_layers_texture.levels = 2; + m_layers_texture.data.assign(m_layers_texture.width * m_layers_texture.height * 5, 0); + } + + bool level_of_detail_2nd_level = true; + m_layers_texture.cells = Slic3r::generate_layer_height_texture( + *m_slicing_parameters, + Slic3r::generate_object_layers(*m_slicing_parameters, m_layer_height_profile), + m_layers_texture.data.data(), m_layers_texture.height, m_layers_texture.width, level_of_detail_2nd_level); + m_layers_texture.valid = true; +} + +void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D & canvas) +{ + if (last_object_id >= 0) { + wxGetApp().plater()->take_snapshot("Variable layer height - Manual edit"); + const_cast(m_model_object)->layer_height_profile.set(m_layer_height_profile); + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().obj_list()->update_info_items(last_object_id); + } +} + +void GLCanvas3D::LayersEditing::update_slicing_parameters() +{ + if (m_slicing_parameters == nullptr) { + m_slicing_parameters = new SlicingParameters(); + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object, m_object_max_z); + } +} + +float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D & canvas) +{ + return +#if ENABLE_RETINA_GL + canvas.get_canvas_size().get_scale_factor() +#else + canvas.get_wxglcanvas()->GetContentScaleFactor() +#endif + * THICKNESS_BAR_WIDTH; +} + Size::Size() : m_width(0) , m_height(0) @@ -535,6 +1017,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, wxTimerEvent); wxDEFINE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_CUSTOMEVT_TICKSCHANGED, wxCommandEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); +wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25; const double GLCanvas3D::DefaultCameraZoomToBedMarginFactor = 2.00; @@ -704,6 +1189,10 @@ bool GLCanvas3D::init() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": before m_layers_editing init"; + if (m_main_toolbar.is_enabled()) + m_layers_editing.init(); + // on linux the gl context is not valid until the canvas is not shown on screen // we defer the geometry finalization of volumes until the first call to render() m_volumes.finalize_geometry(true); @@ -891,6 +1380,7 @@ void GLCanvas3D::update_instance_printable_state_for_objects(const std::vectortake_snapshot("Variable layer height - Reset"); + m_layers_editing.reset_layer_height_profile(*this); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} + +void GLCanvas3D::adaptive_layer_height_profile(float quality_factor) +{ + wxGetApp().plater()->take_snapshot("Variable layer height - Adaptive"); + m_layers_editing.adaptive_layer_height_profile(*this, quality_factor); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} + +void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params) +{ + wxGetApp().plater()->take_snapshot("Variable layer height - Smooth all"); + m_layers_editing.smooth_layer_height_profile(*this, smoothing_params); + m_layers_editing.state = LayersEditing::Completed; + m_dirty = true; +} + bool GLCanvas3D::is_reload_delayed() const { return m_reload_delayed; } +void GLCanvas3D::enable_layers_editing(bool enable) +{ + m_layers_editing.set_enabled(enable); + set_as_dirty(); +} + void GLCanvas3D::enable_legend_texture(bool enable) { m_gcode_viewer.enable_legend(enable); @@ -1333,6 +1863,9 @@ void GLCanvas3D::render(bool only_init) // Negative coordinate means out of the window, likely because the window was deactivated. // In that case the tooltip should be hidden. if (m_mouse.position.x() >= 0. && m_mouse.position.y() >= 0.) { + if (tooltip.empty()) + tooltip = m_layers_editing.get_tooltip(*this); + if (tooltip.empty()) tooltip = m_gizmos.get_tooltip(); @@ -1367,12 +1900,7 @@ void GLCanvas3D::render(bool only_init) right_margin = SLIDER_RIGHT_MARGIN; bottom_margin = SLIDER_BOTTOM_MARGIN; } - #if ENABLE_RETINA_GL - float sc = m_retina_helper->get_scale_factor(); - wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width(), bottom_margin * sc, right_margin); - #else - wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width(), bottom_margin, right_margin); - #endif + wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width(), bottom_margin, right_margin); } wxGetApp().imgui()->render(); @@ -2023,7 +2551,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // If no object is selected, deactivate the active gizmo, if any // Otherwise it may be shown after cleaning the scene (if it was active while the objects were deleted) m_gizmos.reset_all_states(); - // BBS #if 0 // If no object is selected, reset the objects manipulator on the sidebar @@ -2902,6 +3429,22 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) wxWakeUpIdle(); #endif /* __WXMSW__ */ + // Performs layers editing updates, if enabled + if (is_layers_editing_enabled()) { + int object_idx_selected = m_selection.get_object_idx(); + if (object_idx_selected != -1) { + // A volume is selected. Test, whether hovering over a layer thickness bar. + if (m_layers_editing.bar_rect_contains(*this, (float)evt.GetX(), (float)evt.GetY())) { + // Adjust the width of the selection. + m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f); + if (m_canvas != nullptr) + m_canvas->Refresh(); + + return; + } + } + } + // Inform gizmos about the event so they have the opportunity to react. if (m_gizmos.on_mouse_wheel(evt)) return; @@ -2917,6 +3460,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) void GLCanvas3D::on_timer(wxTimerEvent& evt) { + if (m_layers_editing.state == LayersEditing::Editing) + _perform_layer_editing_action(); } void GLCanvas3D::on_render_timer(wxTimerEvent& evt) @@ -3140,6 +3685,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.is_running()) { _deactivate_arrange_menu(); _deactivate_orient_menu(); + _deactivate_layersediting_menu(); } if (wxWindow::FindFocus() != m_canvas) // Grab keyboard focus for input in gizmo dialogs. @@ -3192,6 +3738,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) bool any_gizmo_active = m_gizmos.get_current() != nullptr; int selected_object_idx = m_selection.get_object_idx(); + int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; + + if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) { m_mouse.drag.move_requires_threshold = false; m_mouse.set_move_start_threshold_position_2D_as_invalid(); @@ -3238,9 +3787,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) return; } + // If user pressed left or right button we first check whether this happened on a volume or not. + m_layers_editing.state = LayersEditing::Unknown; + if (layer_editing_object_idx != -1 && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) { + // A volume is selected and the mouse is inside the layer thickness bar. + // Start editing the layer height. + m_layers_editing.state = LayersEditing::Editing; + _perform_layer_editing_action(&evt); + } + // BBS: define Alt key to enable volume selection mode m_selection.set_volume_selection_mode(evt.AltDown() ? Selection::Volume : Selection::Instance); - if (evt.LeftDown() && evt.ShiftDown() && m_picking_enabled) { + if (evt.LeftDown() && evt.ShiftDown() && m_picking_enabled && m_layers_editing.state != LayersEditing::Editing) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::Seam @@ -3306,7 +3864,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } - else if (evt.Dragging() && evt.LeftIsDown() && m_mouse.drag.move_volume_idx != -1) { + else if (evt.Dragging() && evt.LeftIsDown() && m_mouse.drag.move_volume_idx != -1 && m_layers_editing.state == LayersEditing::Unknown) { if (m_canvas_type != ECanvasType::CanvasAssembleView) { if (!m_mouse.drag.move_requires_threshold) { m_mouse.dragging = true; @@ -3364,8 +3922,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Dragging()) { m_mouse.dragging = true; + if (m_layers_editing.state != LayersEditing::Unknown && layer_editing_object_idx != -1) { + if (m_layers_editing.state == LayersEditing::Editing) { + _perform_layer_editing_action(&evt); + m_mouse.position = pos.cast(); + } + } // do not process the dragging if the left mouse was set down in another canvas - if (evt.LeftIsDown()) { + else if (evt.LeftIsDown()) { // if dragging over blank area with left button, rotate if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) { const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); @@ -3466,7 +4030,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_rotation_center(0) = m_rotation_center(1) = m_rotation_center(2) = 0.f; } - if (m_mouse.drag.move_volume_idx != -1 && m_mouse.dragging) { + if (m_layers_editing.state != LayersEditing::Unknown) { + m_layers_editing.state = LayersEditing::Unknown; + _stop_timer(); + m_layers_editing.accept_changes(*this); + } + else if (m_mouse.drag.move_volume_idx != -1 && m_mouse.dragging) { do_move(L("Move Object")); // BBS //wxGetApp().obj_manipul()->set_dirty(); @@ -3474,7 +4043,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // of the scene with the background processing data should be performed. post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); } - else if (evt.LeftUp() && m_picking_enabled && m_rectangle_selection.is_dragging()) { + else if (evt.LeftUp() && m_picking_enabled && m_rectangle_selection.is_dragging() && m_layers_editing.state != LayersEditing::Editing) { //BBS: don't use alt as de-select //if (evt.ShiftDown() || evt.AltDown()) if (evt.ShiftDown()) @@ -3482,13 +4051,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_rectangle_selection.stop_dragging(); } - else if (evt.LeftUp() && !m_mouse.ignore_left_up && !m_mouse.dragging && m_hover_volume_idxs.empty() && m_hover_plate_idxs.empty()) { + else if (evt.LeftUp() && !m_mouse.ignore_left_up && !m_mouse.dragging && m_hover_volume_idxs.empty() && m_hover_plate_idxs.empty() && !is_layers_editing_enabled()) { // deselect and propagate event through callback if (!evt.ShiftDown() && (!any_gizmo_active || !evt.CmdDown()) && m_picking_enabled) deselect_all(); } //BBS Select plate in this 3D canvas. - else if (evt.LeftUp() && !m_mouse.dragging && m_picking_enabled && !m_hover_plate_idxs.empty() && (m_canvas_type == CanvasView3D)) + else if (evt.LeftUp() && !m_mouse.dragging && m_picking_enabled && !m_hover_plate_idxs.empty() && (m_canvas_type == CanvasView3D) && !is_layers_editing_enabled()) { int hover_idx = m_hover_plate_idxs.front(); wxGetApp().plater()->select_plate_by_hover_id(hover_idx); @@ -3497,7 +4066,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_hover_volume_idxs.empty()) deselect_all(); } - else if (evt.RightUp()) { + else if (evt.RightUp() && !is_layers_editing_enabled()) { m_mouse.position = pos.cast(); // forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while // the context menu is already shown @@ -5193,6 +5762,23 @@ bool GLCanvas3D::_init_main_toolbar() if (!m_main_toolbar.add_item(item)) return false; + item.name = "layersediting"; + item.icon_filename = "toolbar_variable_layer_height.svg"; + item.tooltip = _utf8(L("Variable layer height")); + item.sprite_id++; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; + item.visibility_callback = [this]()->bool { + bool res = current_printer_technology() == ptFFF; + // turns off if changing printer technology + if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting")) + force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); + + return res; + }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; + if (!m_main_toolbar.add_item(item)) + return false; + return true; } @@ -5721,6 +6307,10 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with m_camera_clipping_plane = m_gizmos.get_clipping_plane(); + if (m_picking_enabled) + // Update the layer editing selection to the first object selected, update the current object maximum Z. + m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); + if (const BuildVolume &build_volume = m_bed.build_volume(); build_volume.valid()) { switch (build_volume.type()) { case BuildVolume::Type::Rectangle: { @@ -5776,25 +6366,34 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with case GLVolumeCollection::ERenderType::Opaque: { const GLGizmosManager& gm = get_gizmos_manager(); - // GLGizmosManager::EType type = gm.get_current_type(); if (dynamic_cast(gm.get_current()) == nullptr) { - /*if (wxGetApp().plater()->is_wireframe_enabled()) { - if (wxGetApp().plater()->is_show_wireframe()) - shader->set_uniform("show_wireframe", true); - else - shader->set_uniform("show_wireframe", false); - }*/ - //BBS:add assemble view related logic - // do not cull backfaces to show broken geometry, if any - m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this, canvas_type](const GLVolume& volume) { - if (canvas_type == ECanvasType::CanvasAssembleView) { - return !volume.is_modifier && !volume.is_wipe_tower; - } - else { - return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); - } - }, with_outline); + if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { + int object_id = m_layers_editing.last_object_id; + m_volumes.render(type, false, wxGetApp().plater()->get_camera().get_view_matrix(), [object_id](const GLVolume& volume) { + // Which volume to paint without the layer height profile shader? + return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); + }); + m_layers_editing.render_volumes(*this, m_volumes); + } + else { + /*if (wxGetApp().plater()->is_wireframe_enabled()) { + if (wxGetApp().plater()->is_show_wireframe()) + shader->set_uniform("show_wireframe", true); + else + shader->set_uniform("show_wireframe", false); + }*/ + //BBS:add assemble view related logic + // do not cull backfaces to show broken geometry, if any + m_volumes.render(type, m_picking_enabled, wxGetApp().plater()->get_camera().get_view_matrix(), [this, canvas_type](const GLVolume& volume) { + if (canvas_type == ECanvasType::CanvasAssembleView) { + return !volume.is_modifier && !volume.is_wipe_tower; + } + else { + return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); + } + }, with_outline); + } } else { // In case a painting gizmo is open, it should render the painted triangles @@ -6066,6 +6665,9 @@ void GLCanvas3D::_render_overlays() //move gizmos behind of main _render_gizmos_overlay(); + if (m_layers_editing.last_object_id >= 0 && m_layers_editing.object_max_z() > 0.0f) + m_layers_editing.render_overlay(*this); + const ConfigOptionEnum* opt = dynamic_cast*>(m_config->option>("print_sequence")); bool sequential_print = opt != nullptr && (opt->value == PrintSequence::ByObject); std::vector sorted_instances; @@ -7083,6 +7685,29 @@ void GLCanvas3D::_update_volumes_hover_state() } } +void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) +{ + int object_idx_selected = m_layers_editing.last_object_id; + if (object_idx_selected == -1) + return; + + // A volume is selected. Test, whether hovering over a layer thickness bar. + if (evt != nullptr) { + const Rect& rect = LayersEditing::get_bar_rect_screen(*this); + float b = rect.get_bottom(); + m_layers_editing.last_z = m_layers_editing.object_max_z() * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); + m_layers_editing.last_action = + evt->ShiftDown() ? (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE) : + (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE); + } + + m_layers_editing.adjust_layer_height_profile(); + _refresh_if_shown_on_screen(); + + // Automatic action on mouse down with the same coordinate. + _start_timer(); +} + Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) { if (m_canvas == nullptr) @@ -7904,6 +8529,17 @@ bool GLCanvas3D::_deactivate_orient_menu() return false; } +//BBS: add deactivate layersediting menu +bool GLCanvas3D::_deactivate_layersediting_menu() +{ + if (m_main_toolbar.is_item_pressed("layersediting")) { + m_main_toolbar.force_left_action(m_main_toolbar.get_item_id("layersediting"), *this); + return true; + } + + return false; +} + bool GLCanvas3D::_deactivate_collapse_toolbar_items() { GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 452cd0add..b442c4af8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -188,6 +188,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, wxTimerEvent); wxDECLARE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_CUSTOMEVT_TICKSCHANGED, wxCommandEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event); +wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent); class GLCanvas3D { @@ -202,6 +205,108 @@ class GLCanvas3D static void update_render_colors(); static void load_render_colors(); + class LayersEditing + { + public: + enum EState : unsigned char + { + Unknown, + Editing, + Completed, + Num_States + }; + + static const float THICKNESS_BAR_WIDTH; + + private: + bool m_enabled{ false }; + unsigned int m_z_texture_id{ 0 }; + // Not owned by LayersEditing. + const DynamicPrintConfig* m_config{ nullptr }; + // ModelObject for the currently selected object (Model::objects[last_object_id]). + const ModelObject* m_model_object{ nullptr }; + // Maximum z of the currently selected object (Model::objects[last_object_id]). + float m_object_max_z{ 0.0f }; + // Owned by LayersEditing. + SlicingParameters* m_slicing_parameters{ nullptr }; + std::vector m_layer_height_profile; + + mutable float m_adaptive_quality{ 0.5f }; + mutable HeightProfileSmoothingParams m_smooth_params; + + static float s_overlay_window_width; + + struct LayersTexture + { + // Texture data + std::vector data; + // Width of the texture, top level. + size_t width{ 0 }; + // Height of the texture, top level. + size_t height{ 0 }; + // For how many levels of detail is the data allocated? + size_t levels{ 0 }; + // Number of texture cells allocated for the height texture. + size_t cells{ 0 }; + // Does it need to be refreshed? + bool valid{ false }; + }; + LayersTexture m_layers_texture; + + public: + EState state{ Unknown }; + float band_width{ 2.0f }; + float strength{ 0.005f }; + int last_object_id{ -1 }; + float last_z{ 0.0f }; + LayerHeightEditActionType last_action{ LAYER_HEIGHT_EDIT_ACTION_INCREASE }; + + LayersEditing() = default; + ~LayersEditing(); + + void init(); + + void set_config(const DynamicPrintConfig* config); + void select_object(const Model& model, int object_id); + + bool is_allowed() const; + + bool is_enabled() const; + void set_enabled(bool enabled); + + void show_tooltip_information(const GLCanvas3D& canvas, std::map captions_texts, float x, float y); + void render_variable_layer_height_dialog(const GLCanvas3D& canvas); + void render_overlay(const GLCanvas3D& canvas); + void render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection& volumes); + + void adjust_layer_height_profile(); + void accept_changes(GLCanvas3D& canvas); + void reset_layer_height_profile(GLCanvas3D& canvas); + void adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor); + void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params); + + static float get_cursor_z_relative(const GLCanvas3D& canvas); + static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); + static Rect get_bar_rect_screen(const GLCanvas3D& canvas); + static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); + static float get_overlay_window_width() { return LayersEditing::s_overlay_window_width; } + + float object_max_z() const { return m_object_max_z; } + + std::string get_tooltip(const GLCanvas3D& canvas) const; + + private: + bool is_initialized() const; + void generate_layer_height_texture(); + + void render_background_texture(const GLCanvas3D& canvas, const Rect& bar_rect); + void render_curve(const Rect& bar_rect); + + void update_slicing_parameters(); + + static float thickness_bar_width(const GLCanvas3D& canvas); + }; + struct Mouse { struct Drag @@ -394,6 +499,7 @@ private: unsigned int m_last_w, m_last_h; bool m_in_render; wxTimer m_timer; + LayersEditing m_layers_editing; Mouse m_mouse; GLGizmosManager m_gizmos; //BBS: GUI refactor: GLToolbar @@ -655,8 +761,16 @@ public: BoundingBoxf3 scene_bounding_box() const; BoundingBoxf3 plate_scene_bounding_box(int plate_idx) const; + bool is_layers_editing_enabled() const; + bool is_layers_editing_allowed() const; + + void reset_layer_height_profile(); + void adaptive_layer_height_profile(float quality_factor); + void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params); + bool is_reload_delayed() const; + void enable_layers_editing(bool enable); void enable_legend_texture(bool enable); void enable_picking(bool enable); void enable_moving(bool enable); @@ -914,6 +1028,8 @@ public: bool is_object_sinking(int object_idx) const; + void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); + // Convert the screen space coordinate to an object space coordinate. // If the Z screen space coordinate is not provided, a depth buffer value is substituted. Vec3d _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); @@ -1030,9 +1146,11 @@ private: bool _deactivate_arrange_menu(); //BBS: add deactivate_orient_menu bool _deactivate_orient_menu(); + //BBS: add _deactivate_layersediting_menu + bool _deactivate_layersediting_menu(); // BBS FIXME - float get_overlay_window_width() { return 100.f; } + float get_overlay_window_width() { return 0; /*LayersEditing::get_overlay_window_width();*/ } static std::vector> _parse_colors(const std::vector& colors); }; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 1c98d0648..970e1b0f2 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -41,13 +41,13 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); //BBS: add clone event wxDEFINE_EVENT(EVT_GLTOOLBAR_CLONE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent); -wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_FILLCOLOR, IntEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SELECT_SLICED_PLATE, wxCommandEvent); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index d60dfcb8f..ae3b91f88 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -41,6 +41,7 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_ARRANGE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_COPY, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_PASTE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent); //BBS: add clone event wxDECLARE_EVENT(EVT_GLTOOLBAR_CLONE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 7e2f8d772..d5b14c574 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -160,6 +160,22 @@ void View3D::mirror_selection(Axis axis) m_canvas->mirror_selection(axis); } +bool View3D::is_layers_editing_enabled() const +{ + return (m_canvas != nullptr) ? m_canvas->is_layers_editing_enabled() : false; +} + +bool View3D::is_layers_editing_allowed() const +{ + return (m_canvas != nullptr) ? m_canvas->is_layers_editing_allowed() : false; +} + +void View3D::enable_layers_editing(bool enable) +{ + if (m_canvas != nullptr) + m_canvas->enable_layers_editing(enable); +} + bool View3D::is_dragging() const { return (m_canvas != nullptr) ? m_canvas->is_dragging() : false; diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index a61faf157..88e133664 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -66,6 +66,10 @@ public: void center_selected(); void mirror_selection(Axis axis); + bool is_layers_editing_enabled() const; + bool is_layers_editing_allowed() const; + void enable_layers_editing(bool enable); + bool is_dragging() const; bool is_reload_delayed() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 66f7111f4..0ddeb3e74 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -475,6 +475,7 @@ void GLGizmoFdmSupports::show_tooltip_information(float caption_max, float x, fl float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, ImGui::GetStyle().FramePadding.y }); ImGui::ImageButton3(normal_id, hover_id, button_size); if (ImGui::IsItemHovered()) { @@ -506,7 +507,7 @@ void GLGizmoFdmSupports::show_tooltip_information(float caption_max, float x, fl ImGui::EndTooltip(); } - ImGui::PopStyleVar(1); + ImGui::PopStyleVar(2); } // BBS diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 629e92520..a221d4485 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -306,6 +306,7 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, ImGui::GetStyle().FramePadding.y }); ImGui::ImageButton3(normal_id, hover_id, button_size); if (ImGui::IsItemHovered()) { @@ -336,7 +337,7 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x for (const auto &t : tip_items) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); ImGui::EndTooltip(); } - ImGui::PopStyleVar(1); + ImGui::PopStyleVar(2); } void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 0e96fec84..c62ca77d6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -132,6 +132,7 @@ void GLGizmoSeam::show_tooltip_information(float caption_max, float x, float y) float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, ImGui::GetStyle().FramePadding.y }); ImGui::ImageButton3(normal_id, hover_id, button_size); if (ImGui::IsItemHovered()) { @@ -145,7 +146,7 @@ void GLGizmoSeam::show_tooltip_information(float caption_max, float x, float y) for (const auto &t : std::array{"enforce", "block", "remove", "cursor_size", "clipping_of_view"}) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); ImGui::EndTooltip(); } - ImGui::PopStyleVar(1); + ImGui::PopStyleVar(2); } void GLGizmoSeam::tool_changed(wchar_t old_tool, wchar_t new_tool) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index c47aed6ba..fd2fe7cb7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -256,6 +256,12 @@ public: else return nullptr; } + void* get_icon_texture_id(MENU_ICON_NAME icon) const{ + if (icon_list.find((int)icon) != icon_list.end()) + return icon_list.at(icon); + else + return nullptr; + } Vec3d get_flattening_normal() const; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 0da971614..9ed406452 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -2099,7 +2099,7 @@ void NotificationManager::render_notifications(GLCanvas3D &canvas, float overlay for (const auto& notification : m_pop_notifications) { if (notification->get_state() != PopNotification::EState::Hidden) { - notification->render(canvas, last_y, m_move_from_overlay && !m_in_preview, overlay_width, right_margin * m_scale); + notification->render(canvas, last_y * m_scale, m_move_from_overlay && !m_in_preview, overlay_width * m_scale, right_margin * m_scale); if (notification->get_state() != PopNotification::EState::Finished) last_y = notification->get_top() + GAP_WIDTH; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 718c03b79..0a4951563 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1707,6 +1707,8 @@ struct Plater::priv bool is_sidebar_collapsed() const { return sidebar->is_collapsed(); } void collapse_sidebar(bool collapse); + bool is_view3D_layers_editing_enabled() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_layers_editing_enabled(); } + void set_current_canvas_as_dirty(); GLCanvas3D* get_current_canvas3D(); void unbind_canvas_event_handlers(); @@ -1873,6 +1875,7 @@ struct Plater::priv void on_action_del_plate(SimpleEvent&); void on_action_split_objects(SimpleEvent&); void on_action_split_volumes(SimpleEvent&); + void on_action_layersediting(SimpleEvent&); void on_object_select(SimpleEvent&); void on_right_click(RBtnEvent&); @@ -1926,6 +1929,7 @@ struct Plater::priv bool can_split_to_objects() const; bool can_split_to_volumes() const; bool can_arrange() const; + bool can_layers_editing() const; bool can_fix_through_netfabb() const; bool can_simplify() const; bool can_set_instance_to_object() const; @@ -1982,6 +1986,8 @@ struct Plater::priv bool PopupObjectTable(int object_id, int volume_id, const wxPoint& position); void on_action_send_to_printer(); private: + bool layers_height_allowed() const; + void update_fff_scene(); void update_sla_scene(); @@ -2147,7 +2153,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) if (wxGetApp().is_editor()) { // 3DScene events: - view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); + view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { + delayed_error_message.clear(); + this->background_process_timer.Start(500, wxTIMER_ONE_SHOT); + }); view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); //BBS: add part plate related logic @@ -2191,6 +2200,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); view3D_canvas->Bind(EVT_GLCANVAS_COLLAPSE_SIDEBAR, [this](SimpleEvent&) { this->q->collapse_sidebar(!this->q->is_sidebar_collapsed()); }); + view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); + view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); + view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->reload_all_from_disk(); }); // 3DScene/Toolbar: @@ -2213,6 +2225,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_CUT, [q](SimpleEvent&) { q->cut_selection_to_clipboard(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); }); + view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); //BBS: add clone view3D_canvas->Bind(EVT_GLTOOLBAR_CLONE, [q](SimpleEvent&) { q->clone_selection(); }); view3D_canvas->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); }); @@ -3667,6 +3680,12 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { + // if the selection is not valid to allow for layer editing, we need to turn off the tool if it is running + if (!layers_height_allowed() && view3D->is_layers_editing_enabled()) { + SimpleEvent evt(EVT_GLTOOLBAR_LAYERSEDITING); + on_action_layersediting(evt); + } + // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); } @@ -3713,6 +3732,9 @@ void Plater::priv::deselect_all() void Plater::priv::remove(size_t obj_idx) { + if (view3D->is_layers_editing_enabled()) + view3D->enable_layers_editing(false); + m_ui_jobs.cancel_all(); model.delete_object(obj_idx); //BBS: notify partplate the instance removed @@ -3746,6 +3768,9 @@ void Plater::priv::delete_all_objects_from_model() { Plater::TakeSnapshot snapshot(q, "Delete All Objects"); + if (view3D->is_layers_editing_enabled()) + view3D->enable_layers_editing(false); + reset_gcode_toolpaths(); gcode_result.reset(); @@ -3776,6 +3801,9 @@ void Plater::priv::reset(bool apply_presets_change) set_project_filename(""); + if (view3D->is_layers_editing_enabled()) + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); + reset_gcode_toolpaths(); //BBS: update gcode to current partplate's //GCodeProcessorResult* current_result = this->background_process.get_current_plate()->get_slice_result(); @@ -6591,6 +6619,26 @@ bool Plater::priv::can_arrange() const return !model.objects.empty() && !m_ui_jobs.is_any_running(); } +bool Plater::priv::layers_height_allowed() const +{ + if (printer_technology != ptFFF) + return false; + + int obj_idx = get_selected_object_idx(); + return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->bounding_box().max.z() > SINKING_Z_THRESHOLD && view3D->is_layers_editing_allowed(); +} + +bool Plater::priv::can_layers_editing() const +{ + return layers_height_allowed(); +} + +void Plater::priv::on_action_layersediting(SimpleEvent&) +{ + view3D->enable_layers_editing(!view3D->is_layers_editing_enabled()); + notification_manager->set_move_from_overlay(view3D->is_layers_editing_enabled()); +} + void Plater::priv::enter_gizmos_stack() { assert(m_undo_redo_stack_active == &m_undo_redo_stack_main); @@ -6635,6 +6683,8 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed UndoRedo::SnapshotData snapshot_data; snapshot_data.snapshot_type = snapshot_type; snapshot_data.printer_technology = this->printer_technology; + if (this->view3D->is_layers_editing_enabled()) + snapshot_data.flags |= UndoRedo::SnapshotData::VARIABLE_LAYER_EDITING_ACTIVE; if (this->sidebar->obj_list()->is_selected(itSettings)) { snapshot_data.flags |= UndoRedo::SnapshotData::SELECTED_SETTINGS_ON_SIDEBAR; snapshot_data.layer_range_idx = this->sidebar->obj_list()->get_selected_layers_range_idx(); @@ -6776,6 +6826,8 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator unsigned int new_flags = it_snapshot->snapshot_data.flags; UndoRedo::SnapshotData top_snapshot_data; top_snapshot_data.printer_technology = this->printer_technology; + if (this->view3D->is_layers_editing_enabled()) + top_snapshot_data.flags |= UndoRedo::SnapshotData::VARIABLE_LAYER_EDITING_ACTIVE; if (this->sidebar->obj_list()->is_selected(itSettings)) { top_snapshot_data.flags |= UndoRedo::SnapshotData::SELECTED_SETTINGS_ON_SIDEBAR; top_snapshot_data.layer_range_idx = this->sidebar->obj_list()->get_selected_layers_range_idx(); @@ -6794,6 +6846,10 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator if (this->view3D->get_canvas3d()->get_gizmos_manager().wants_reslice_supports_on_undo()) top_snapshot_data.flags |= UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS; + // Disable layer editing before the Undo / Redo jump. + if (!new_variable_layer_editing_active && view3D->is_layers_editing_enabled()) + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); + // Make a copy of the snapshot, undo/redo could invalidate the iterator const UndoRedo::Snapshot snapshot_copy = *it_snapshot; // Do the jump in time. @@ -6866,6 +6922,9 @@ void Plater::priv::undo_redo_to(std::vector::const_iterator this->sidebar->obj_list()->set_selected_layers_range_idx(layer_range_idx); this->update_after_undo_redo(snapshot_copy, temp_snapshot_was_taken); + // Enable layer editing after the Undo / Redo jump. + if (!view3D->is_layers_editing_enabled() && this->layers_height_allowed() && new_variable_layer_editing_active) + view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting")); } dirty_state.update_from_undo_redo_stack(m_undo_redo_stack_main.project_modified()); @@ -10745,6 +10804,7 @@ bool Plater::can_simplify() const { return p->can_simplify(); } bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); } bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); } bool Plater::can_arrange() const { return p->can_arrange(); } +bool Plater::can_layers_editing() const { return p->can_layers_editing(); } bool Plater::can_paste_from_clipboard() const { if (!IsShown() || !p->is_view3D_shown()) return false; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index fd41630fb..0561f2f5e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -447,6 +447,7 @@ public: bool can_arrange() const; //BBS bool can_cut_to_clipboard() const; + bool can_layers_editing() const; bool can_paste_from_clipboard() const; bool can_copy_to_clipboard() const; bool can_undo() const;