From 0c78224f1a356ad9eabef120e43b1690bfc659ff Mon Sep 17 00:00:00 2001 From: QIDI TECH <893239786@qq.com> Date: Wed, 17 Sep 2025 09:42:58 +0800 Subject: [PATCH] update libslic3r --- src/imgui/imconfig.h | 1 + src/imgui/imgui.h | 6 + src/imgui/imgui_widgets.cpp | 151 ++++++++ .../LineSegmentation/LineSegmentation.cpp | 9 +- src/libslic3r/AppConfig.cpp | 29 +- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Calib.cpp | 2 + src/libslic3r/Calib.hpp | 1 + src/libslic3r/CommonDefs.hpp | 21 ++ src/libslic3r/Config.cpp | 13 +- src/libslic3r/Config.hpp | 46 ++- src/libslic3r/Emboss.cpp | 36 +- src/libslic3r/Emboss.hpp | 7 +- src/libslic3r/EmbossShape.hpp | 1 + src/libslic3r/Extruder.cpp | 2 +- src/libslic3r/Fill/Fill.cpp | 2 +- src/libslic3r/Format/qds_3mf.cpp | 12 +- src/libslic3r/FuzzySkin.cpp | 1 + src/libslic3r/GCode.cpp | 116 +++++-- src/libslic3r/GCode.hpp | 10 +- src/libslic3r/GCode/GCodeEditor.cpp | 4 + src/libslic3r/GCode/GCodeProcessor.cpp | 160 +++++++-- src/libslic3r/GCode/GCodeProcessor.hpp | 58 +++- .../GCode/RetractWhenCrossingPerimeters.cpp | 85 ++++- .../GCode/RetractWhenCrossingPerimeters.hpp | 20 +- src/libslic3r/GCode/TimelapsePosPicker.cpp | 16 +- src/libslic3r/GCode/TimelapsePosPicker.hpp | 2 +- src/libslic3r/GCode/ToolOrdering.cpp | 14 +- src/libslic3r/GCode/WipeTower.cpp | 52 ++- src/libslic3r/GCode/WipeTower.hpp | 5 +- src/libslic3r/Geometry/VoronoiUtilsCgal.cpp | 2 + src/libslic3r/Model.cpp | 1 + src/libslic3r/OverhangDetector.cpp | 13 +- src/libslic3r/Preset.cpp | 72 +++- src/libslic3r/Preset.hpp | 4 +- src/libslic3r/PresetBundle.cpp | 183 +++++++++- src/libslic3r/PresetBundle.hpp | 6 + src/libslic3r/Print.cpp | 236 +++++++++---- src/libslic3r/PrintApply.cpp | 2 + src/libslic3r/PrintConfig.cpp | 327 +++++++++++++++--- src/libslic3r/PrintConfig.hpp | 26 +- src/libslic3r/PrintObject.cpp | 57 +-- src/libslic3r/Support/SupportCommon.cpp | 4 +- src/libslic3r/Support/TreeSupport.cpp | 23 +- src/libslic3r/TriangleSelector.cpp | 2 +- src/libslic3r/TriangleSelector.hpp | 2 +- src/libslic3r/Utils.hpp | 51 ++- src/platform/unix/build_appimage.sh.in | 2 +- 48 files changed, 1570 insertions(+), 326 deletions(-) create mode 100644 src/libslic3r/CommonDefs.hpp diff --git a/src/imgui/imconfig.h b/src/imgui/imconfig.h index 188e265..7588032 100644 --- a/src/imgui/imconfig.h +++ b/src/imgui/imconfig.h @@ -172,6 +172,7 @@ namespace ImGui const wchar_t SphereButtonIcon = 0x0816; const wchar_t GapFillIcon = 0x0817; const wchar_t ConfirmIcon = 0x0818; + const wchar_t HelioIcon = 0x0819; const wchar_t MinimalizeDarkButton = 0x081C; const wchar_t MinimalizeHoverDarkButton = 0x081D; diff --git a/src/imgui/imgui.h b/src/imgui/imgui.h index 47d6edf..81eda8d 100644 --- a/src/imgui/imgui.h +++ b/src/imgui/imgui.h @@ -622,6 +622,12 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height IMGUI_API bool QDTImageSelectable(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& font_size, int font_line, const ImVec4& tint_col, const ImVec2& uv0, const ImVec2& uv1, bool selected = false); IMGUI_API bool QDTSelectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height + IMGUI_API bool QDTSelectable_LeftImage( + const char * label, + bool selected = false, + ImTextureID texture_id =nullptr, + ImGuiSelectableFlags flags = 0, + const ImVec2 &size = ImVec2(0, 0)); IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. // Widgets: List Boxes diff --git a/src/imgui/imgui_widgets.cpp b/src/imgui/imgui_widgets.cpp index 26deb89..9cc3fb1 100644 --- a/src/imgui/imgui_widgets.cpp +++ b/src/imgui/imgui_widgets.cpp @@ -7087,6 +7087,157 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl return pressed; } +bool ImGui::QDTSelectable_LeftImage(const char *label, bool selected, ImTextureID texture_id,ImGuiSelectableFlags flags, const ImVec2 &size_arg) +{ + ImGuiWindow *window = GetCurrentWindow(); + if (window->SkipItems) return false; + + ImGuiContext & g = *GImGui; + const ImGuiStyle &style = g.Style; + + // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle. + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrLineTextBaseOffset; + ItemSize(size, 0.0f); + + // Fill horizontal space + // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets. + const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0; + const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x; + const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x); + + // Text stays at the submission position, but bounding box may be extended on both sides + const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); + const ImVec2 text_min = ImVec2(pos.x + arrow_size, pos.y); + const ImVec2 text_max(min_x + size.x, pos.y + size.y); + + // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. + ImRect bb(min_x, pos.y, text_max.x, text_max.y); + if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { + const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; + const float spacing_y = style.ItemSpacing.y; + const float spacing_L = IM_FLOOR(spacing_x * 0.50f); + const float spacing_U = IM_FLOOR(spacing_y * 0.50f); + bb.Min.x -= spacing_L; + bb.Min.y -= spacing_U; + bb.Max.x += (spacing_x - spacing_L); + bb.Max.y += (spacing_y - spacing_U); + } + // if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } + + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + if (span_all_columns) { + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + } + + bool item_add; + if (flags & ImGuiSelectableFlags_Disabled) { + ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; + g.CurrentItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus; + item_add = ItemAdd(bb, id); + g.CurrentItemFlags = backup_item_flags; + } else { + item_add = ItemAdd(bb, id); + } + + if (span_all_columns) { + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } + + if (!item_add) + return false; + // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, + // which would be advantageous since most selectable are not selected. + if (span_all_columns && window->DC.CurrentColumns) + PushColumnsBackground(); + else if (span_all_columns && g.CurrentTable) + TablePushBackgroundChannel(); + + // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } + if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } + if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } + if (flags & ImGuiSelectableFlags_Disabled) { button_flags |= ImGuiButtonFlags_Disabled; } + if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } + if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } + + if (flags & ImGuiSelectableFlags_Disabled) selected = false; + + const bool was_selected = selected; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + if (hovered || g.ActiveId == id) { + ImGui::PushStyleColor(ImGuiCol_Border, GetColorU32(ImGuiCol_BorderActive)); + if (arrow_size == 0) { + RenderFrameBorder(bb.Min, ImVec2(bb.Max.x - style.WindowPadding.x, bb.Max.y), style.FrameRounding); + } else { + RenderFrameBorder(ImVec2(bb.Min.x + style.WindowPadding.x, bb.Min.y), ImVec2(bb.Max.x - style.WindowPadding.x, bb.Max.y), style.FrameRounding); + } + ImGui::PopStyleColor(1); + } + // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard + if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) { + if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { + SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, ImRect(bb.Min - window->Pos, bb.Max - window->Pos)); + g.NavDisableHighlight = true; + } + } + if (pressed) MarkItemEdited(id); + + if (flags & ImGuiSelectableFlags_AllowItemOverlap) SetItemAllowOverlap(); + + // In this branch, Selectable() cannot toggle the selection so this will never trigger. + if (selected != was_selected) //-V547 + window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_ToggledSelection; + + // Render + if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) hovered = true; + if (hovered || selected) { + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + if (arrow_size == 0) { + RenderFrame(bb.Min, ImVec2(bb.Max.x - style.WindowPadding.x, bb.Max.y), col, false, 0.0f); + } else { + RenderFrame(ImVec2(bb.Min.x + style.WindowPadding.x, bb.Min.y), ImVec2(bb.Max.x - style.WindowPadding.x, bb.Max.y), col, false, 0.0f); + } + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + } + + if (span_all_columns && window->DC.CurrentColumns) + PopColumnsBackground(); + else if (span_all_columns && g.CurrentTable) + TablePopBackgroundChannel(); + + if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); + + // Automatically close popups + if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.CurrentItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) + CloseCurrentPopup(); + if (texture_id) { + auto line_height = ImGui::GetFrameHeight(); + ImRect bb_image; + bb_image.Min = bb.Min; + bb_image.Min.x += std::min(10.f, line_height * 0.45f); + bb_image.Min.y += line_height * 0.2; + auto image_height = line_height * 0.6; + bb_image.Max.x = bb_image.Min.x + image_height; + bb_image.Max.y = bb_image.Min.y + image_height; + ImGui::GetWindowDrawList()->AddImage(texture_id, bb_image.Min, bb_image.Max); + } + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, window->DC.LastItemStatusFlags); + return pressed; +} + bool ImGui::QDTImageSelectable(ImTextureID user_texture_id, const ImVec2& size_arg, const ImVec2& font_size, int font_line, const ImVec4& tint_col, const ImVec2& uv0, const ImVec2& uv1, bool selected) { ImGuiWindow* window = GetCurrentWindow(); diff --git a/src/libslic3r/Algorithm/LineSegmentation/LineSegmentation.cpp b/src/libslic3r/Algorithm/LineSegmentation/LineSegmentation.cpp index f04517d..287f542 100644 --- a/src/libslic3r/Algorithm/LineSegmentation/LineSegmentation.cpp +++ b/src/libslic3r/Algorithm/LineSegmentation/LineSegmentation.cpp @@ -222,7 +222,14 @@ std::optional create_line_region_range(ClipperLib_Z::Path &&int ZAttributes curr_z(intersection[curr_idx]); if (!prev_z.is_clip_point && !curr_z.is_clip_point) { - if ((prev_z.point_index > curr_z.point_index) && (pre_pos != cur_pos)){ + // There may be repeated intersections on different line segments + int max_point_idx = subject.size() - 1; + bool is_valid_order = prev_z.point_index <= curr_z.point_index; + if ((curr_z.point_index == max_point_idx) && (prev_z.point_index == 0)) + is_valid_order = false; + if ((curr_z.point_index == 0) && (prev_z.point_index == max_point_idx)) + is_valid_order = true; + if (!is_valid_order && (pre_pos != cur_pos)) { return true; } else if (curr_z.point_index == prev_z.point_index) { assert(curr_z.point_index < subject.size()); diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 842e9da..db8886e 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -176,8 +176,6 @@ void AppConfig::set_defaults() set_bool("enable_merge_color_by_sync_ams", false); if (get("ams_sync_match_full_use_color_dist").empty()) set_bool("ams_sync_match_full_use_color_dist", false); - if (get("enable_sidebar_resizable").empty()) - set_bool("enable_sidebar_resizable", true); if (get("enable_sidebar_floatable").empty()) set_bool("enable_sidebar_floatable", false); @@ -215,6 +213,8 @@ void AppConfig::set_defaults() if (get("enable_multi_machine").empty()) set_bool("enable_multi_machine", false); + if (get("enable_record_gcodeviewer_option_item").empty()) + set_bool("enable_record_gcodeviewer_option_item", false); if (get("prefer_to_use_dgpu").empty()) set_bool("prefer_to_use_dgpu", false); @@ -379,7 +379,19 @@ void AppConfig::set_defaults() } if (get("mouse_wheel").empty()) { - set("mouse_wheel", "0"); + set("mouse_wheel", "0"); } + + // helio options + if (get("helio_enable").empty()) { + set_bool("helio_enable", false); + } + + if (get("helio_api_china").empty()) { + set("helio_api_china", "https://api.helioam.cn/graphql"); + } + + if (get("helio_api_other").empty()) { + set("helio_api_other", "https://api.helioadditive.com/graphql"); } if (get("max_recent_count").empty()) { @@ -441,6 +453,10 @@ void AppConfig::set_defaults() if (get("print", "bed_leveling").empty()) { set_str("print", "bed_leveling", "1"); } + //y71 + if (get("print", "enable_multi_box").empty()) { + set_str("print", "enable_multi_box", "0"); + } if (get("print", "flow_cali").empty()) { set_str("print", "flow_cali", "1"); } @@ -466,6 +482,13 @@ void AppConfig::set_defaults() if (get("play_tpu_printing_video").empty()) { set_bool("play_tpu_printing_video", true); } + if (get("show_wrapping_detect_dialog").empty()) { + set_bool("show_wrapping_detect_dialog", true); + } + + if (get("prompt_for_brittle_filaments").empty()){ + set_bool("prompt_for_brittle_filaments", true); + } // 10 if (get("machine_list_net").empty()) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c04340d..384f644 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -62,6 +62,7 @@ set(lisbslic3r_sources Clipper2Utils.hpp Color.cpp Color.hpp + CommonDefs.hpp Config.cpp Config.hpp CurveAnalyzer.cpp diff --git a/src/libslic3r/Calib.cpp b/src/libslic3r/Calib.cpp index d9a1d17..255d4fa 100644 --- a/src/libslic3r/Calib.cpp +++ b/src/libslic3r/Calib.cpp @@ -505,6 +505,8 @@ CalibPressureAdvancePattern::CalibPressureAdvancePattern(const Calib_Params &par void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfig &config, bool is_qdt_machine, Model &model, const Vec3d &origin) { + if (model.objects.empty()) + return; std::stringstream gcode; gcode << "; start pressure advance pattern for layer\n"; diff --git a/src/libslic3r/Calib.hpp b/src/libslic3r/Calib.hpp index 488354d..6359c5e 100644 --- a/src/libslic3r/Calib.hpp +++ b/src/libslic3r/Calib.hpp @@ -15,6 +15,7 @@ enum class CalibMode : int { Calib_PA_Line, Calib_PA_Pattern, Calib_PA_Tower, + Calib_Auto_PA_Line, Calib_Flow_Rate, Calib_Temp_Tower, Calib_Vol_speed_Tower, diff --git a/src/libslic3r/CommonDefs.hpp b/src/libslic3r/CommonDefs.hpp new file mode 100644 index 0000000..559583b --- /dev/null +++ b/src/libslic3r/CommonDefs.hpp @@ -0,0 +1,21 @@ +#pragma once + +// CommonDefs.hpp +// --------------- +// This header provides common definitions and enumerations shared across multiple libraries. +// It is intended for use in projects that require consistent type definitions, such as nozzle types. +// The contents of this file are designed to be reusable and maintainable for cross-library integration. + +namespace Slic3r +{ + // QDS + enum NozzleType + { + ntUndefine = 0, + ntHardenedSteel, + ntStainlessSteel, + ntTungstenCarbide, + ntBrass, + ntCount + }; +} \ No newline at end of file diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 7b3a8fd..a887366 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -456,8 +456,12 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys auto opt_key2 = opt_key.substr(0, n); auto my_opt2 = dynamic_cast(this->option(opt_key2)); auto other_opt = other.option(opt_key2); - if (my_opt2 == nullptr && other_opt) + if (my_opt2 == nullptr && other_opt) { my_opt2 = dynamic_cast(this->option(opt_key2, true)); + if (my_opt2->empty()) { + my_opt2->resize(1, other_opt); + } + } if (my_opt2) { int index = std::atoi(opt_key.c_str() + n + 1); if (other_opt) @@ -1371,18 +1375,19 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo bool begin_found = false; bool end_found = false; std::string line; - while (std::getline(ifs, line)) - if (line == "; CONFIG_BLOCK_START") { + while (std::getline(ifs, line)) { + if ( boost::starts_with(line, "; CONFIG_BLOCK_START")) { begin_found = true; break; } + } if (!begin_found) { //BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Configuration block closing tag \"; CONFIG_BLOCK_START\" not found when reading %1%", file); throw Slic3r::RuntimeError(format("Config tag \"; CONFIG_BLOCK_START\" not found")); } std::string key, value; while (std::getline(ifs, line)) { - if (line == "; CONFIG_BLOCK_END") { + if (boost::starts_with(line, "; CONFIG_BLOCK_END")) { end_found = true; break; } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 9666bad..8d3fb34 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -356,6 +356,7 @@ public: virtual void append(const ConfigOption *rhs) = 0; virtual void set(const ConfigOption* rhs, size_t start, size_t len) = 0; virtual void set_with_restore(const ConfigOptionVectorBase* rhs, std::vector& restore_index, int stride) = 0; + virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector& restore_index, int start, int len) = 0; virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector& diff_index, int stride) = 0; virtual void set_to_index(const ConfigOptionVectorBase* rhs, std::vector& dest_index, int stride) = 0; virtual void set_with_nil(const ConfigOptionVectorBase* rhs, const ConfigOptionVectorBase* inherits, int stride) = 0; @@ -506,7 +507,50 @@ public: } } else - throw ConfigurationError("ConfigOptionVector::set_with_keep(): Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector::set_with_restore(): Assigning an incompatible type"); + } + + //set a item related with extruder variants when loading config from filament json, replace the original filament items + //rhs: item from seperate filament config + //restore_index: which index in this vector need to be restored + //start: which index in this vector need to be replaced + //count: how many items in this vector need to be replaced + virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector& restore_index, int start, int len) override + { + if (rhs->type() == this->type()) { + //backup original ones + std::vector backup_values = this->values; + + if (this->values.size() < start) { + T v = this->values.front(); + this->values.resize(start, v); + //throw ConfigurationError("ConfigOptionVector::set_with_restore_2(): invalid size found"); + } + else { + if (this->values.size() < start + len) + len = this->values.size() - start; + + //erase the original ones + if (len > 0) + this->values.erase(this->values.begin() + start, this->values.begin() + start + len); + } + + // Assign the new value from the rhs vector. + auto other = static_cast*>(rhs); + + if (other->values.size() != (restore_index.size())) + throw ConfigurationError("ConfigOptionVector::set_with_restore_2(): Assigning from an vector with invalid restore_index size"); + + for (size_t i = 0; i < restore_index.size(); i++) { + if ((restore_index[i] != -1)&&(restore_index[i] < backup_values.size())) { + this->values.insert(this->values.begin() + start + i, backup_values[restore_index[i]]); + } + else + this->values.insert(this->values.begin() + start + i, other->values[i]); + } + } + else + throw ConfigurationError("ConfigOptionVector::set_with_restore_2(): Assigning an incompatible type"); } //set a item related with extruder variants when loading user config, only set the different value of some extruder diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 8c7d6ed..5dc0a03 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -794,7 +794,6 @@ const Glyph* get_glyph( // scale glyph size glyph.advance_width = static_cast(glyph.advance_width / SHAPE_SCALE); glyph.left_side_bearing = static_cast(glyph.left_side_bearing / SHAPE_SCALE); - if (!glyph.shape.empty()) { if (font_prop.boldness.has_value()) { float delta = static_cast(*font_prop.boldness / SHAPE_SCALE / font_prop.size_in_mm); @@ -1237,6 +1236,7 @@ ExPolygons letter2shapes(wchar_t letter, Point &cursor, FontFileWithCache &font_ } void letter2shapes(ExPolygons & result, + float cur_scale, FontFileWithCache &real_use_font, float & real_scale, wchar_t letter, @@ -1275,7 +1275,7 @@ void letter2shapes(ExPolygons & result, // Create glyph from font file and cache it const Glyph *glyph_ptr = (it != cache.end()) ? &it->second : get_glyph(unicode, font, font_prop, cache, font_info_cache); - if (glyph_ptr == nullptr) { + if (glyph_ptr == nullptr || glyph_ptr->shape.size() == 0) { bool can_gen_from_back_font = false; if (bfc_fn) { auto fn_result = bfc_fn(); @@ -1309,8 +1309,12 @@ void letter2shapes(ExPolygons & result, // move glyph to cursor position ExPolygons expolygons = glyph_ptr->shape; // copy for (ExPolygon &expolygon : expolygons) expolygon.translate(cursor); - - cursor.x() += glyph_ptr->advance_width; + if (real_scale > 0) { + auto new_advance_width = glyph_ptr->advance_width * real_scale / cur_scale; + cursor.x() += new_advance_width; + } else { + cursor.x() += glyph_ptr->advance_width; + } result = expolygons; } @@ -1396,12 +1400,12 @@ HealedExPolygons Slic3r::Emboss::text2shapes(EmbossShape & emboss FontFileWithCache & font_with_cache, const char * text, const FontProp & font_prop, + double standard_scale, const std::function &was_canceled, - BackFontCacheFn bfc_fn, - double standard_scale) + BackFontCacheFn bfc_fn) {//for CreateFontImageJob std::wstring text_w = boost::nowide::widen(text); - text2vshapes(emboss_shape, font_with_cache, text_w, font_prop, was_canceled, bfc_fn); // ExPolygonsWithIds vshapes = + text2vshapes(emboss_shape, font_with_cache, text_w, font_prop, standard_scale, was_canceled, bfc_fn); // ExPolygonsWithIds vshapes = auto &vshapes = emboss_shape.shapes_with_ids; float delta = static_cast(1. / SHAPE_SCALE); if (bfc_fn && standard_scale > 0) { @@ -1440,6 +1444,7 @@ void Slic3r::Emboss::text2vshapes(EmbossShape & emboss_shape, FontFileWithCache & font_with_cache, const std::wstring & text, const FontProp & font_prop, + double standard_scale, const std::function &was_canceled, BackFontCacheFn bfc_fn) { @@ -1451,7 +1456,8 @@ void Slic3r::Emboss::text2vshapes(EmbossShape & emboss_shape, unsigned counter = 0; Point cursor(0, 0); - + std::vector text_cursors; + float last_x = 0.f,cur_x =0.f; fontinfo_opt font_info_cache; ExPolygonsWithIds result; result.reserve(text.size()); @@ -1466,12 +1472,15 @@ void Slic3r::Emboss::text2vshapes(EmbossShape & emboss_shape, if (was_canceled()) return; } + if (letter == wchar_t(' ')) { + letter = 'i'; + } unsigned id = static_cast(letter); float real_scale = -1.f; if (bfc_fn) { // support_backup_fonts ExPolygons exps; FontFileWithCache real_use_font; - letter2shapes(exps, real_use_font,real_scale, letter, cursor, font_with_cache, font_prop, font_info_cache, bfc_fn); + letter2shapes(exps, standard_scale, real_use_font, real_scale, letter, cursor, font_with_cache, font_prop, font_info_cache, bfc_fn); result.push_back({id, exps}); text_scales.emplace_back(real_scale); text_map_font.emplace_back(real_use_font); @@ -1479,6 +1488,9 @@ void Slic3r::Emboss::text2vshapes(EmbossShape & emboss_shape, result.push_back({id, letter2shapes(letter, cursor, font_with_cache, font_prop, font_info_cache)}); text_scales.emplace_back(real_scale); } + cur_x = cursor.x() * standard_scale; + text_cursors.emplace_back(cur_x - last_x); + last_x = cur_x; } if (bfc_fn) {// support_backup_fonts align_shape(result, text_map_font, text, font_prop, font); @@ -1486,6 +1498,12 @@ void Slic3r::Emboss::text2vshapes(EmbossShape & emboss_shape, align_shape(result, text, font_prop, font); } emboss_shape.text_scales = text_scales; + emboss_shape.text_cursors = text_cursors; + for (int i = 0; i < result.size(); i++) { + if (text[i] == wchar_t(' ')) { + result[i].expoly.clear(); + } + } emboss_shape.shapes_with_ids = result; } diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 204b7bb..1613838 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -80,16 +80,17 @@ namespace Emboss FontFileWithCache & font_with_cache, const char *text, const FontProp &font_prop, + double standard_scale, const std::function &was_canceled = []() { return false; }, - BackFontCacheFn bfc_fn = nullptr, - double standard_scale = -1); + BackFontCacheFn bfc_fn = nullptr); void text2vshapes( EmbossShape & emboss_shape, FontFileWithCache & font_with_cache, const std::wstring & text, const FontProp & font_prop, + double standard_scale, const std::function &was_canceled = []() { return false; }, - BackFontCacheFn bfc_fn = nullptr); + BackFontCacheFn bfc_fn = nullptr); HealedExPolygons union_with_delta(ExPolygons expoly, float delta, unsigned max_heal_iteration); const unsigned ENTER_UNICODE = static_cast('\n'); diff --git a/src/libslic3r/EmbossShape.hpp b/src/libslic3r/EmbossShape.hpp index 187548b..4ea6d2b 100644 --- a/src/libslic3r/EmbossShape.hpp +++ b/src/libslic3r/EmbossShape.hpp @@ -188,6 +188,7 @@ struct EmbossShape // When embossing shape is made by svg file this is source data std::optional svg_file; std::vector text_scales; + std::vector text_cursors; // undo / redo stack recovery template void save(Archive &ar) const { diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index 9edb116..4599779 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -9,7 +9,7 @@ std::vector Extruder::m_share_retracted = {0.,0.}; Extruder::Extruder(unsigned int id, GCodeConfig *config, bool share_extruder) : m_id(id), m_config(config), - m_share_extruder(m_share_extruder) + m_share_extruder(share_extruder) { reset(); diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 6325a93..bdfec83 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -565,7 +565,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // Create the filler object. std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); - f->layer_id = this->id(); + f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers. f->z = this->print_z; f->angle = surface_fill.params.angle; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; diff --git a/src/libslic3r/Format/qds_3mf.cpp b/src/libslic3r/Format/qds_3mf.cpp index b46df7c..a0968c2 100644 --- a/src/libslic3r/Format/qds_3mf.cpp +++ b/src/libslic3r/Format/qds_3mf.cpp @@ -1371,7 +1371,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } lock{&archive}; if (!open_zip_reader(&archive, filename)) { - add_error("Unable to open the file"+filename); + add_error("Unable to open the file " + PathSanitizer::sanitize(filename)); return false; } @@ -1629,7 +1629,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } lock{ &archive }; if (!open_zip_reader(&archive, filename)) { - add_error("Unable to open the file"+filename); + add_error("Unable to open the file " + PathSanitizer::sanitize(filename)); return false; } @@ -5721,7 +5721,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) boost::system::error_code ec; boost::filesystem::remove(filepath_tmp, ec); if (!open_zip_writer(&archive, filepath_tmp)) { - add_error("Unable to open the file"+filepath_tmp); + add_error("Unable to open the file " + PathSanitizer::sanitize(filepath_tmp)); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n"); return false; } @@ -5792,7 +5792,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } if (!open_zip_writer(&archive, filename)) { - add_error("Unable to open the file"+filename); + add_error("Unable to open the file " + PathSanitizer::sanitize(filename)); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n"); return false; } @@ -8209,10 +8209,10 @@ bool _QDS_3MF_Exporter::_add_auxiliary_dir_to_archive(mz_zip_archive &archive, c std::replace(dst_in_3mf.begin(), dst_in_3mf.end(), '\\', '/'); if (_3MF_COVER_FILE.compare(1, _3MF_COVER_FILE.length() - 1, dst_in_3mf) == 0) { data._3mf_thumbnail = dst_in_3mf; - } else if (m_thumbnail_small.compare(1, m_thumbnail_small.length() - 1, dst_in_3mf) == 0) { + } else if (!m_thumbnail_small.empty() && m_thumbnail_small.compare(1, m_thumbnail_small.length() - 1, dst_in_3mf) == 0) { data._3mf_printer_thumbnail_small = dst_in_3mf; if (m_thumbnail_middle == m_thumbnail_small) data._3mf_printer_thumbnail_middle = dst_in_3mf; - } else if (m_thumbnail_middle.compare(1, m_thumbnail_middle.length() - 1, dst_in_3mf) == 0) { + } else if (!m_thumbnail_middle.empty() && m_thumbnail_middle.compare(1, m_thumbnail_middle.length() - 1, dst_in_3mf) == 0) { data._3mf_printer_thumbnail_middle = dst_in_3mf; } result &= _add_file_to_archive(archive, dst_in_3mf, src_file); diff --git a/src/libslic3r/FuzzySkin.cpp b/src/libslic3r/FuzzySkin.cpp index 7f03924..ce1c2d4 100644 --- a/src/libslic3r/FuzzySkin.cpp +++ b/src/libslic3r/FuzzySkin.cpp @@ -1,4 +1,5 @@ #include +#include #include "libslic3r/Algorithm/LineSegmentation/LineSegmentation.hpp" #include "libslic3r/Arachne/utils/ExtrusionJunction.hpp" diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 90f03b5..611e76f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1120,7 +1120,7 @@ static std::vector get_path_of_change_filament(const Print& print) wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; } - if (m_enable_timelapse_print && m_is_first_print) { + if ((m_enable_timelapse_print || m_enable_wrapping_detection) && m_is_first_print) { gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][0], m_tool_changes[m_layer_idx][0].new_tool, wipe_tower_z); m_tool_change_idx++; m_is_first_print = false; @@ -1149,7 +1149,7 @@ static std::vector get_path_of_change_filament(const Print& print) ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); } - if (m_enable_timelapse_print && m_is_first_print) { + if ((m_enable_timelapse_print || m_enable_wrapping_detection) && m_is_first_print) { return false; } @@ -1435,6 +1435,7 @@ namespace DoExport { if (ret.size() < MAX_TAGS_COUNT) check(_(L("Before layer change G-code")), config.before_layer_change_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Layer change G-code")), config.layer_change_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Time lapse G-code")), config.time_lapse_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_(L("Time lapse G-code")), config.wrapping_detection_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change filament G-code")), config.change_filament_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value); //if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value); @@ -1627,15 +1628,14 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.result().filament_printable_reuslt = FilamentPrintableResult(conflict_filament, bed_type_to_gcode_string(m_config.curr_bed_type)); } m_processor.set_filaments(m_writer.extruders()); - // check gcode is valid in multi_extruder printabele area + // check gcode is valid in machine printabele area and multi_extruder printabele area int extruder_size = m_print->config().nozzle_diameter.values.size(); - if (extruder_size > 1) { - std::vector extruder_unprintable_polys = m_print->get_extruder_unprintable_polygons(); - m_processor.check_multi_extruder_gcode_valid(extruder_unprintable_polys, - m_print->get_extruder_printable_height(), - m_print->get_filament_maps(), - m_print->get_physical_unprintable_filaments(m_print->get_slice_used_filaments(false))); - } + std::vector extruder_unprintable_polys = m_print->get_extruder_unprintable_polygons(); + Pointfs plate_printable_area = m_print->config().printable_area.values; + Pointfs wrapping_exclude_area_points = m_print->config().wrapping_exclude_area.values; + m_processor.check_multi_extruder_gcode_valid(extruder_size, plate_printable_area, m_print->config().printable_height.value, wrapping_exclude_area_points, + extruder_unprintable_polys, m_print->get_extruder_printable_height(), m_print->get_filament_maps(), + m_print->get_physical_unprintable_filaments(m_print->get_slice_used_filaments(false))); m_processor.finalize(true); // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); @@ -2703,7 +2703,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } else { file.write(this->retract()); } - file.write(m_writer.travel_to_z(m_max_layer_z)); + file.write(m_writer.travel_to_z(m_max_layer_z + m_writer.config.z_hop.get_at(initial_extruder_id))); file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); m_enable_cooling_markers = true; // Disable motion planner when traveling to first object point. @@ -2764,7 +2764,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Prusa Multi-Material wipe tower. if (has_wipe_tower && !layers_to_print.empty()) { m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(), - print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); + print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get(), print.get_slice_used_filaments(false))); m_wipe_tower->set_wipe_tower_depth(print.get_wipe_tower_depth()); m_wipe_tower->set_wipe_tower_bbx(print.get_wipe_tower_bbx()); m_wipe_tower->set_rib_offset(print.get_rib_offset()); @@ -2985,7 +2985,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato [temp_cfg_flush_multiplier_idx](double inputx) { return inputx * temp_cfg_flush_multiplier_idx; }); } print_cfg_temp.option("flush_volumes_matrix")->values = temp_flush_volumes_matrix; - } else if (filament_count_tmp == 1) { + } else if (filament_count_tmp == 1 || print.calib_params().mode != CalibMode::Calib_None) { }// Not applicable to flush matrix situations else { // flush_volumes_matrix value count error? @@ -4398,6 +4398,30 @@ GCode::LayerResult GCode::process_layer( if (m_wipe_tower) m_wipe_tower->set_is_first_print(true); + auto insert_wrapping_detection_gcode = [this, &print, &print_z, &most_used_extruder]() -> std::string { + std::string wrapping_gcode; + if (print.config().enable_wrapping_detection && !print.config().wrapping_detection_gcode.value.empty()) { + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); + config.set_key_value("most_used_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(most_used_extruder))); + config.set_key_value("curr_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(m_writer.filament()->extruder_id()))); + wrapping_gcode = this->placeholder_parser_process("wrapping_detection_gcode", print.config().wrapping_detection_gcode.value, m_writer.filament()->id(), &config) +"\n"; + } + m_writer.set_current_position_clear(false); + + double temp_z_after_tool_change; + if (GCodeProcessor::get_last_z_from_gcode(wrapping_gcode, temp_z_after_tool_change)) { + Vec3d pos = m_writer.get_position(); + pos(2) = temp_z_after_tool_change; + m_writer.set_position(pos); + } + return wrapping_gcode; + }; + + bool has_insert_wrapping_detection_gcode = false; + // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { @@ -4419,6 +4443,12 @@ GCode::LayerResult GCode::process_layer( has_insert_timelapse_gcode = true; } } + + if (print.config().enable_wrapping_detection && !has_insert_wrapping_detection_gcode) { + gcode += this->retract(false, false, auto_lift_type, true); + gcode += insert_wrapping_detection_gcode(); + has_insert_wrapping_detection_gcode = true; + } gcode += m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()); } } else { @@ -4431,6 +4461,13 @@ GCode::LayerResult GCode::process_layer( gcode += insert_timelapse_gcode(); has_insert_timelapse_gcode = true; } + + if (print.config().enable_wrapping_detection && !has_insert_wrapping_detection_gcode) { + gcode += this->retract(false, false, auto_lift_type, true); + gcode += insert_wrapping_detection_gcode(); + has_insert_wrapping_detection_gcode = true; + } + gcode += this->set_extruder(extruder_id, print_z); } @@ -4989,7 +5026,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou const double clip_length = m_enable_loop_clipping && !enable_seam_slope ? seam_gap : 0; // get paths ExtrusionPaths paths; - bool set_holes_and_compensation_speed = loop.get_customize_flag() && !loop.has_overhang_paths(); + bool set_holes_and_compensation_speed = loop.get_customize_flag() == CustomizeFlag::cfCircleCompensation && !loop.has_overhang_paths(); if (set_holes_and_compensation_speed && m_config.apply_scarf_seam_on_circles.value) { enable_seam_slope = true; } @@ -5061,8 +5098,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou //new_loop.target_speed = get_path_speed(new_loop.starts.back()); //new_loop.slowdown_slope_speed(); // QDS: smooth speed of discontinuity areas - if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && loop.is_set_speed_discontinuity_area()) + if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && loop.is_set_speed_discontinuity_area()) { + // set smoothing_cof + set_smooth_coff(FILAMENT_CONFIG(filament_velocity_adaptation_factor)); smooth_speed_discontinuity_area(new_loop.paths); + } // Then extrude it for (const auto &p : new_loop.get_all_paths()) { //w16 @@ -5089,8 +5129,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou if (!enable_seam_slope || slope_has_overhang) { //1.9.5 // QDS: smooth speed of discontinuity areas - if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && loop.is_set_speed_discontinuity_area()) + if (m_config.detect_overhang_wall && m_config.smooth_speed_discontinuity_area && loop.is_set_speed_discontinuity_area()){ + // set smoothing_cof + set_smooth_coff(FILAMENT_CONFIG(filament_velocity_adaptation_factor)); smooth_speed_discontinuity_area(paths); + } for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { //w16 @@ -5409,12 +5452,12 @@ double GCode::mapping_speed(double dist) { if (dist <= 0) return 0; - return this->config().smooth_coefficient * pow(dist, 2); + return m_smooth_coefficient * pow(dist, 2); } double GCode::get_speed_coor_x(double speed){ - double temp = speed / this->config().smooth_coefficient; + double temp = speed / m_smooth_coefficient; return sqrt(temp); } @@ -5737,7 +5780,7 @@ ExtrusionPaths GCode::set_speed_transition(std::vector &paths) void GCode::smooth_speed_discontinuity_area(ExtrusionPaths &paths) { - if (paths.size() <= 1 || this->config().smooth_coefficient == 0) + if (paths.size() <= 1 || m_smooth_coefficient == 0) return; //step 1 merge same speed path @@ -5796,6 +5839,28 @@ bool GCode::slowDownByHeight(double& maxSpeed, double& maxAcc, const ExtrusionPa return do_slowdown_by_height; } +double GCode::calc_max_volumetric_speed(const double layer_height, const double line_width, const std::string co_str) +{ + std::vector cs; + std::stringstream ss(co_str); + std::string token; + + while (std::getline(ss, token, ' ')) { + try { + cs.push_back(std::stod(token)); + } catch (...) { + std::cerr << "Transformation failed: " << token << std::endl; + } + } + if (cs.size() != 6 || std::all_of(cs.begin(), cs.end(), [](double v) { return v == 0; })) return std::numeric_limits::max(); + + const double x = layer_height; + const double y = line_width; + + double res = cs[0] * x * x + cs[1] * y * y + cs[2] * x * y + cs[3] * x + cs[4] * y + cs[5]; + return res; +} + std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double speed, bool use_seperate_speed, bool is_first_slope) { std::string gcode; @@ -5974,6 +6039,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } //QDS: if not set the speed, then use the filament_max_volumetric_speed directly double filament_max_volumetric_speed = FILAMENT_CONFIG(filament_max_volumetric_speed); + if (FILAMENT_CONFIG(filament_adaptive_volumetric_speed)){ + double fitted_value = calc_max_volumetric_speed(path.height, path.width, FILAMENT_CONFIG(volumetric_speed_coefficients)); + filament_max_volumetric_speed = std::min(filament_max_volumetric_speed, fitted_value); + } + if( speed == 0 ) { if (_mm3_per_mm>0) @@ -6277,8 +6347,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string // generate G-code for the travel move std::string gcode; if (needs_retraction) { - if (m_config.reduce_crossing_wall && could_be_wipe_disabled && !m_last_scarf_seam_flag) - m_wipe.reset_path(); + if (m_config.reduce_crossing_wall && could_be_wipe_disabled && !m_last_scarf_seam_flag) m_wipe.reset_path(); Point last_post_before_retract = this->last_pos(); gcode += this->retract(false, false, lift_type); @@ -6479,9 +6548,10 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp if (support_island.contains(travel)) return false; } + //QDS: need retract when long moving to print perimeter to avoid dropping of material - if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr && - m_config.sparse_infill_density.value > 0 && m_retract_when_crossing_perimeters.travel_inside_internal_regions(*m_layer, travel)) + if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr && m_config.sparse_infill_density.value > 0 && + m_retract_when_crossing_perimeters.travel_inside_internal_regions_no_wall_crossing(*m_layer, travel)) // Skip retraction if travel is contained in an internal slice *and* // internal infill is enabled (so that stringing is entirely not visible). //FIXME any_internal_region_slice_contains() is potentionally very slow, it shall test for the bounding boxes first. diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 2db613c..00f0f92 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -73,7 +73,8 @@ public: const Vec3d plate_origin, const std::vector &priming, const std::vector> &tool_changes, - const WipeTower::ToolChangeResult &final_purge) : + const WipeTower::ToolChangeResult &final_purge, + const std::vector &slice_used_filaments) : m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.prime_tower_width.value)), m_wipe_tower_pos(float(print_config.wipe_tower_x.get_at(plate_idx)), float(print_config.wipe_tower_y.get_at(plate_idx))), @@ -86,6 +87,7 @@ public: m_plate_origin(plate_origin), m_single_extruder_multi_material(print_config.single_extruder_multi_material), m_enable_timelapse_print(print_config.timelapse_type.value == TimelapseType::tlSmooth), + m_enable_wrapping_detection(print_config.enable_wrapping_detection && (print_config.wrapping_exclude_area.values.size() > 2) && (slice_used_filaments.size() <= 1)), m_is_first_print(true), m_print_config(&print_config) { @@ -139,6 +141,7 @@ private: Vec3d m_plate_origin; bool m_single_extruder_multi_material; bool m_enable_timelapse_print; + bool m_enable_wrapping_detection; bool m_is_first_print; const PrintConfig * m_print_config; float m_wipe_tower_depth; @@ -180,6 +183,7 @@ public: // QDS m_toolchange_count(0), m_nominal_z(0.), + m_smooth_coefficient(0.), //w16 m_resonance_avoidance(true) {} @@ -230,7 +234,7 @@ public: bool is_QDT_Printer(); BoundingBoxf first_layer_projection(const Print& print) const; - + void set_smooth_coff(float filamet_melting) { m_smooth_coefficient = filamet_melting * m_config.smooth_coefficient; } // Object and support extrusions of the same PrintObject at the same print_z. // public, so that it could be accessed by free helper functions from GCode.cpp struct LayerToPrint @@ -561,6 +565,7 @@ private: bool m_enable_label_object; std::vector m_label_objects_ids; std::string _encode_label_ids_to_base64(std::vector ids); + float m_smooth_coefficient{0.0f}; // 1 << 0: A1 series cannot supprot traditional timelapse when printing by object (cannot turn on timelapse) // 1 << 1: A1 series cannot supprot traditional timelapse with spiral vase mode (cannot turn on timelapse) @@ -593,6 +598,7 @@ private: int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const; int get_highest_bed_temperature(const bool is_first_layer,const Print &print) const; + double calc_max_volumetric_speed(const double layer_height, const double line_width, const std::string co_str); std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1, bool set_holes_and_compensation_speed = false, bool is_first_slope = false); ExtrusionPaths set_speed_transition(std::vector &paths); void split_and_mapping_speed(double other_path_v, double final_v, ExtrusionPaths &this_path, double max_smooth_length, ExtrusionPaths &interpolated_paths, bool split_from_left = true); diff --git a/src/libslic3r/GCode/GCodeEditor.cpp b/src/libslic3r/GCode/GCodeEditor.cpp index b610341..d5e3779 100644 --- a/src/libslic3r/GCode/GCodeEditor.cpp +++ b/src/libslic3r/GCode/GCodeEditor.cpp @@ -294,6 +294,10 @@ std::vector GCodeEditor::parse_layer_gcode( } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { line.type = CoolingLine::TYPE_EXTRUDE_END; active_speed_modifier = size_t(-1); + } + // y71 + else if (boost::starts_with(sline, "TOOL_CHANGE")) { + ; } else if (boost::starts_with(sline, m_toolchange_prefix)) { unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + m_toolchange_prefix.size()); // Only change extruder in case the number is meaningful. User could provide an out-of-range index through custom gcodes - those shall be ignored. diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 5f3693b..2355b4c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1413,6 +1413,7 @@ void GCodeProcessorResult::reset() { printable_area = Pointfs(); //QDS: add bed exclude area bed_exclude_area = Pointfs(); + wrapping_exclude_area = Pointfs(); //QDS: add toolpath_outside toolpath_outside = false; //QDS: add label_object_enabled @@ -1441,8 +1442,10 @@ void GCodeProcessorResult::reset() { printable_area = Pointfs(); //QDS: add bed exclude area bed_exclude_area = Pointfs(); + wrapping_exclude_area = Pointfs(); //QDS: add toolpath_outside toolpath_outside = false; + is_helio_gcode = false; //QDS: add label_object_enabled label_object_enabled = false; long_retraction_when_cut = false; @@ -1638,13 +1641,19 @@ void GCodeProcessor::register_commands() } } -bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vector &unprintable_areas, +bool GCodeProcessor::check_multi_extruder_gcode_valid(const int extruder_size, + const Pointfs plate_printable_area, + const double plate_printable_height, + const Pointfs wrapping_exclude_area, + const std::vector &unprintable_areas, const std::vector &printable_heights, const std::vector &filament_map, const std::vector> &unprintable_filament_types) { m_result.limit_filament_maps.clear(); - m_result.gcode_check_result.reset(); + m_result.gcode_check_result.reset();// including both single extruder machine printable area check result and multi extruder result + Polygon plate_printable_poly = Polygon::new_scale(plate_printable_area); + Polygon wrapping_exclude_poly = Polygon::new_scale(wrapping_exclude_area); m_result.limit_filament_maps.resize(filament_map.size(), 0); @@ -1656,65 +1665,125 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vector> gcode_path_pos; // object_id, filament_id, pos for (const GCodeProcessorResult::MoveVertex &move : m_result.moves) { - if (move.type == EMoveType::Extrude/* || move.type == EMoveType::Travel*/) { - if (move.is_arc_move_with_interpolation_points()) { - for (int i = 0; i < move.interpolation_points.size(); i++) { - gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.interpolation_points[i].cast())); + // sometimes, the start line extrude was outside the edge of plate a little, this is allowed, so do not include into the gcode_path_pos + if (move.type == EMoveType::Extrude && move.extrusion_role != ExtrusionRole::erFlush /* || move.type == EMoveType::Travel*/) + if (move.extrusion_role == ExtrusionRole::erCustom) { + if (move.is_arc_move_with_interpolation_points()) { + for (int i = 0; i < move.interpolation_points.size(); i++) { + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(move.interpolation_points[i].cast())); + } + } else { + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(move.position.cast())); } + gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom = + std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom, move.print_z); + } else { + if (move.is_arc_move_with_interpolation_points()) { + for (int i = 0; i < move.interpolation_points.size(); i++) { + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.interpolation_points[i].cast())); + } + } else { + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.position.cast())); + } + gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z = std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z, + move.print_z); } - else { - gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.position.cast())); - } - gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z = std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z, move.print_z); - } } bool valid = true; Point plate_offset = Point(scale_(m_x_offset), scale_(m_y_offset)); + plate_printable_poly.translate(plate_offset); + //wrapping_exclude_poly.translate(plate_offset); + BoundingBox plate_printable_bbox = plate_printable_poly.bounding_box(); + if (plate_printable_poly.is_valid()) { + plate_printable_bbox.offset(scale_(2.0)); // Expand the range to provide a tolerance + } else + plate_printable_bbox.defined = false; //when this is used, the printable area config was missing, something wrong + for (auto obj_iter = gcode_path_pos.begin(); obj_iter != gcode_path_pos.end(); ++obj_iter) { - int object_label_id = obj_iter->first; + int object_label_id = obj_iter->first; const std::map &path_pos = obj_iter->second; for (auto iter = path_pos.begin(); iter != path_pos.end(); ++iter) { int extruder_id = filament_map[iter->first] - 1; - Polygon path_poly(iter->second.pos); + Points iter_points;//temp points + iter_points.insert(iter_points.end(), iter->second.pos.begin(), iter->second.pos.end());// put object/wipetower extrude position in + Polygon path_poly(iter_points); + if (path_poly.empty()) continue; BoundingBox bbox = path_poly.bounding_box(); - - // check printable area - // Simplified use bounding_box, Accurate calculation is not efficient - for (Polygon poly : unprintable_areas[extruder_id]) { - poly.translate(plate_offset); - if (poly.bounding_box().overlap(bbox)) { - m_result.gcode_check_result.error_code = 1; - std::pair filament_to_object_id; - filament_to_object_id.first = iter->first; + if (plate_printable_bbox.defined) { + if (!plate_printable_bbox.contains(bbox)) { // out of the bed area + m_result.gcode_check_result.error_code |= (1<<2); + std::pair filament_to_object_id; + filament_to_object_id.first = iter->first; filament_to_object_id.second = object_label_id; m_result.gcode_check_result.print_area_error_infos[extruder_id].push_back(filament_to_object_id); valid = false; } } - - // check printable height - if (iter->second.max_print_z > printable_heights[extruder_id]) { - m_result.gcode_check_result.error_code |= (1 << 1); + if ( iter->second.max_print_z > plate_printable_height ) { //over height + m_result.gcode_check_result.error_code |= (1 << 3); std::pair filament_to_object_id; filament_to_object_id.first = iter->first; filament_to_object_id.second = object_label_id; m_result.gcode_check_result.print_height_error_infos[extruder_id].push_back(filament_to_object_id); - m_result.limit_filament_maps[iter->first] |= (1 << extruder_id); valid = false; } - for (int i = 0; i < unprintable_areas.size(); ++i) { - for (Polygon poly : unprintable_areas[i]) { - poly.translate(plate_offset); - if (!poly.bounding_box().overlap(bbox)) - continue; + // if (wrapping_exclude_poly.is_valid()) { + // if (wrapping_exclude_poly.bounding_box().overlap(bbox)) { // get into the wrapping area + // m_result.gcode_check_result.error_code |= (1 << 4); + // std::pair filament_to_object_id; + // filament_to_object_id.first = iter->first; + // filament_to_object_id.second = object_label_id; + // m_result.gcode_check_result.print_area_error_infos[extruder_id].push_back(filament_to_object_id); + // valid = false; + // } + // } - m_result.limit_filament_maps[iter->first] |= (1 << i); + if (extruder_size > 1) {// in multi extruder condition + /*//iter_points.insert(iter_points.end(), iter->second.pos_custom.begin(), iter->second.pos_custom.end()); // put custom extrude position in + //Polygon path_poly_custom(iter_points); + //BoundingBox bbox_custom = path_poly_custom.bounding_box(); + //bbox_custom.offset(-scale_(1.0)); // Narrow the range to provide a tolerance for the custom gcode + //bbox.merge(bbox_custom); // merge the custom gcode pos with other pos*/ + // check printable area + // Simplified use bounding_box, Accurate calculation is not efficient + if (!unprintable_areas[extruder_id].empty()) + for (Polygon poly : unprintable_areas[extruder_id]) { + poly.translate(plate_offset); + if (poly.bounding_box().overlap(bbox)) { + m_result.gcode_check_result.error_code |= 1; + std::pair filament_to_object_id; + filament_to_object_id.first = iter->first; + filament_to_object_id.second = object_label_id; + m_result.gcode_check_result.print_area_error_infos[extruder_id].push_back(filament_to_object_id); + valid = false; + } + } + // check printable height + if ((extruder_id < printable_heights.size()) && (iter->second.max_print_z > printable_heights[extruder_id])) { + m_result.gcode_check_result.error_code |= (1 << 1); + std::pair filament_to_object_id; + filament_to_object_id.first = iter->first; + filament_to_object_id.second = object_label_id; + m_result.gcode_check_result.print_height_error_infos[extruder_id].push_back(filament_to_object_id); + m_result.limit_filament_maps[iter->first] |= (1 << extruder_id); + valid = false; + } + + for (int i = 0; i < unprintable_areas.size(); ++i) { + for (Polygon poly : unprintable_areas[i]) { + poly.translate(plate_offset); + if (!poly.bounding_box().overlap(bbox)) continue; + + m_result.limit_filament_maps[iter->first] |= (1 << i); + } } } } @@ -1910,6 +1979,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) if (bed_exclude_area != nullptr) m_result.bed_exclude_area = bed_exclude_area->values; + const ConfigOptionPoints* wrapping_exclude_area = config.option("wrapping_exclude_area"); + if (wrapping_exclude_area != nullptr) + m_result.wrapping_exclude_area = wrapping_exclude_area->values; + const ConfigOptionString* print_settings_id = config.option("print_settings_id"); if (print_settings_id != nullptr) m_result.settings_ids.print = print_settings_id->value; @@ -2228,6 +2301,7 @@ void GCodeProcessor::reset() m_extrusion_role = erNone; + m_is_helio_gcode = false; m_filament_id = {static_cast(-1),static_cast(-1)}; m_last_filament_id = {static_cast(-1),static_cast(-1) }; m_extruder_id = static_cast(-1); @@ -2245,6 +2319,7 @@ void GCodeProcessor::reset() m_enable_pre_heating = false; m_hotend_cooling_rate = m_hotend_heating_rate = { 2.f }; + m_thermal_index = ThermalIndex(0.0, 0.0, 0.0); m_highest_bed_temp = 0; m_extruded_last_z = 0.0f; @@ -2368,7 +2443,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::functionprocess_gcode_line(line, true); }, m_result.lines_ends); - + m_result.update_imgui_flag = true; + m_result.is_helio_gcode = m_is_helio_gcode; // Don't post-process the G-code to update time stamps. this->finalize(false); } @@ -2700,6 +2776,13 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool } } +// Processes the gcode comments added by the helio processing server and stores them +void GCodeProcessor::process_helioadditive_comment(const GCodeReader::GCodeLine &line) +{ + const std::string &comment = line.raw(); + m_thermal_index = parse_helioadditive_comment(comment, m_is_helio_gcode); +} + #if __has_include() template struct is_from_chars_convertible : std::false_type {}; @@ -3640,6 +3723,8 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line) void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) { + process_helioadditive_comment(line); + int filament_id = get_filament_id(); int last_filament_id = get_last_filament_id(); float filament_diameter = (static_cast(filament_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[filament_id] : m_result.filament_diameters.back(); @@ -4420,6 +4505,8 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line) // QDS: this function is absolutely new for G2 and G3 gcode void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line) { + process_helioadditive_comment(line); + int filament_id = get_filament_id(); float filament_diameter = (static_cast(filament_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[filament_id] : m_result.filament_diameters.back(); float filament_radius = 0.5f * filament_diameter; @@ -5551,6 +5638,9 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) m_fan_speed, m_extruder_temps[filament_id], static_cast(m_layer_id), //layer_duration: set later + m_thermal_index.min, + m_thermal_index.max, + m_thermal_index.mean, {0.f,0.f}, // prefix sum of move time to this move : set later //QDS: add plate's offset to the rendering vertices Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[filament_id], diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 6817f71..cf6b81a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -136,7 +137,8 @@ namespace Slic3r { struct GCodeCheckResult { - int error_code = 0; // 0 means succeed, 0001 printable area error, 0010 printable height error + int error_code = 0; // 0 means succeed, 0b 0001 multi extruder printable area error, 0b 0010 multi extruder printable height error, + // 0b 0100 plate printable area error, 0b 1000 plate printable height error, 0b 10000 wrapping detection area error std::map>> print_area_error_infos; // printable_area extruder_id to which cannot printed in this extruder std::map>> print_height_error_infos; // printable_height extruder_id to which cannot printed in this extruder void reset() { @@ -203,6 +205,9 @@ namespace Slic3r { float fan_speed{ 0.0f }; // percentage float temperature{ 0.0f }; // Celsius degrees float layer_duration{ 0.0f }; // s (layer id before finalize) + float thermal_index_min{0.0f}; + float thermal_index_max{0.0f}; + float thermal_index_mean{0.0f}; std::arraytime{ 0.f,0.f }; // prefix sum of time, assigned during finalize() @@ -237,6 +242,7 @@ namespace Slic3r { Pointfs printable_area; //QDS: add bed exclude area Pointfs bed_exclude_area; + Pointfs wrapping_exclude_area; std::vector extruder_areas; std::vector extruder_heights; //QDS: add toolpath_outside @@ -247,6 +253,8 @@ namespace Slic3r { bool long_retraction_when_cut {0}; int timelapse_warning_code {0}; bool support_traditional_timelapse{true}; + bool update_imgui_flag{false}; + bool is_helio_gcode{false}; float printable_height; SettingsIds settings_ids; size_t filaments_count; @@ -287,8 +295,11 @@ namespace Slic3r { lines_ends = other.lines_ends; printable_area = other.printable_area; bed_exclude_area = other.bed_exclude_area; + wrapping_exclude_area = other.wrapping_exclude_area; toolpath_outside = other.toolpath_outside; label_object_enabled = other.label_object_enabled; + update_imgui_flag = other.update_imgui_flag; + is_helio_gcode = other.is_helio_gcode; long_retraction_when_cut = other.long_retraction_when_cut; timelapse_warning_code = other.timelapse_warning_code; printable_height = other.printable_height; @@ -457,10 +468,21 @@ namespace Slic3r { static const std::string Mm3_Per_Mm_Tag; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + struct ThermalIndex + { + float max; + float min; + float mean; + bool isNull; + ThermalIndex() : min(-200), max(-200), mean(-200), isNull(true) {} + + ThermalIndex(float minVal, float maxVal, float meanVal) : min(minVal), max(maxVal), mean(meanVal), isNull(false) {} + }; + bool is_helio_gcode() { return m_is_helio_gcode; } private: - using AxisCoords = std::array; + using AxisCoords = std::array; using ExtruderColors = std::vector; - using ExtruderTemps = std::vector; + using ExtruderTemps = std::vector; enum class EUnits : unsigned char { @@ -1053,6 +1075,8 @@ namespace Slic3r { unsigned char m_extruder_id; ExtruderColors m_extruder_colors; ExtruderTemps m_extruder_temps; + ThermalIndex m_thermal_index; + bool m_is_helio_gcode{false}; int m_highest_bed_temp; float m_extruded_last_z; float m_first_layer_height; // mm @@ -1106,7 +1130,11 @@ namespace Slic3r { GCodeProcessor(); void init_filament_maps_and_nozzle_type_when_import_only_gcode(); // check whether the gcode path meets the filament_map grouping requirements - bool check_multi_extruder_gcode_valid(const std::vector &unprintable_areas, + bool check_multi_extruder_gcode_valid(const int extruder_size, + const Pointfs plate_printable_area, + const double plate_printable_height, + const Pointfs wrapping_exclude_area, + const std::vector &unprintable_areas, const std::vector &printable_heights, const std::vector &filament_map, const std::vector>& unprintable_filament_types ); @@ -1152,12 +1180,34 @@ namespace Slic3r { m_detect_layer_based_on_tag = enabled; } + static ThermalIndex parse_helioadditive_comment(const std::string comment,bool& is_helio) + { + if (boost::algorithm::contains(comment, ";helioadditive=")) { + std::regex regexPattern(R"(\bti\.max=(-?[0-9]*\.?[0-9]+),ti\.min=(-?[0-9]*\.?[0-9]+),ti\.mean=(-?[0-9]*\.?[0-9]+)\b)"); + std::smatch match; + if (std::regex_search(comment, match, regexPattern)) { + float maxVal = std::stof(match[1].str()) * 100.0; + float minVal = std::stof(match[2].str()) * 100.0; + float meanVal = std::stof(match[3].str()) * 100.0; + is_helio = true; + return ThermalIndex(minVal, maxVal, meanVal); + } else { + std::cerr << "Error: Unable to parse thermal index values from comment." << std::endl; + return ThermalIndex(); + } + + } else { + return ThermalIndex(); + } + }; + private: void register_commands(); void apply_config(const DynamicPrintConfig& config); void apply_config_simplify3d(const std::string& filename); void apply_config_superslicer(const std::string& filename); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); + void process_helioadditive_comment(const GCodeReader::GCodeLine& line); // Process tags embedded into comments void process_tags(const std::string_view comment, bool producers_enabled); diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp index 776091a..81f42f5 100644 --- a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.cpp @@ -1,14 +1,77 @@ #include "../ClipperUtils.hpp" #include "../Layer.hpp" #include "../Polyline.hpp" +#include "../BoundingBox.hpp" +#include "../ExPolygon.hpp" +#include "../Polygon.hpp" +#include "./Utils.hpp" +#include #include "RetractWhenCrossingPerimeters.hpp" +// #define RETRACT_WHEN_CROSSING_PERIMETERS_DEBUG + namespace Slic3r { +bool RetractWhenCrossingPerimeters::travel_cross_perimeters(const Layer &layer, const Polyline &travel) +{ + if (!cross_perimeters_flag) { + cross_perimeters_flag = true; + // get all the external perimeters and internal perimeters + m_internal_islands_lines.clear(); + Polylines perimeters_polylines; + for (const auto &layer_region_ptr : layer.regions()) { + bool is_internal = false; + for (const Surface &surface : layer_region_ptr->get_slices().surfaces) + if (surface.is_internal()) { + is_internal = true; + auto lines = surface.expolygon.lines(); + m_internal_islands_lines.insert(m_internal_islands_lines.end(), std::make_move_iterator(lines.begin()), std::make_move_iterator(lines.end())); + } + if (is_internal) layer_region_ptr->perimeters.collect_polylines(perimeters_polylines); + } + for (const auto &perimeter_polyline : perimeters_polylines) { + // Convert Polyline to Lines and add to m_internal_islands_lines. + auto lines = perimeter_polyline.lines(); + m_internal_islands_lines.insert(m_internal_islands_lines.end(), std::make_move_iterator(lines.begin()), std::make_move_iterator(lines.end())); + } + + m_aabbtree_lines_distancer = AABBTreeLines::LinesDistancer{std::move(m_internal_islands_lines)}; + +#ifdef RETRACT_WHEN_CROSSING_PERIMETERS_DEBUG + m_internal_islands_bbox = BoundingBox(); + for (const auto &line : m_internal_islands_lines) { + // Update the bounding box of internal islands. + m_internal_islands_bbox.merge(get_extents({line.a, line.b})); + } + m_internal_islands_bbox.offset(SCALED_EPSILON); +#endif // RETRACT_WHEN_CROSSING_PERIMETERS_DEBUG + } + +#ifdef RETRACT_WHEN_CROSSING_PERIMETERS_DEBUG + static int travel_idx = 0; + SVG svg(debug_out_path("travel_cross_perimeters_layer_%d_travel_%d.svg", layer.id(), travel_idx++), m_internal_islands_bbox); + svg.draw(travel, "blue"); + for (const auto &perimeter_line : m_aabbtree_lines_distancer.get_lines()) { svg.draw(perimeter_line, "green"); } +#endif // RETRACT_WHEN_CROSSING_PERIMETERS_DEBUG + + bool has_intersection = false; + for (const auto &line : travel.lines()) { + // Check if the travel line intersects with any of the internal islands. + auto intersections = m_aabbtree_lines_distancer.intersections_with_line(line); + if (!intersections.empty()) { + has_intersection = true; + break; + } + } + + return has_intersection; +} + bool RetractWhenCrossingPerimeters::travel_inside_internal_regions(const Layer &layer, const Polyline &travel) { if (m_layer != &layer) { + cross_perimeters_flag = false; // Update cache. m_layer = &layer; m_internal_islands.clear(); @@ -16,25 +79,21 @@ bool RetractWhenCrossingPerimeters::travel_inside_internal_regions(const Layer & // Collect expolygons of internal slices. for (const LayerRegion *layerm : layer.regions()) for (const Surface &surface : layerm->get_slices().surfaces) - if (surface.is_internal()) - m_internal_islands.emplace_back(&surface.expolygon); + if (surface.is_internal()) m_internal_islands.emplace_back(&surface.expolygon); // Calculate bounding boxes of internal slices. std::vector bboxes; bboxes.reserve(m_internal_islands.size()); - for (size_t i = 0; i < m_internal_islands.size(); ++ i) - bboxes.emplace_back(i, get_extents(*m_internal_islands[i])); + for (size_t i = 0; i < m_internal_islands.size(); ++i) bboxes.emplace_back(i, get_extents(*m_internal_islands[i])); // Build AABB tree over bounding boxes of internal slices. m_aabbtree_internal_islands.build_modify_input(bboxes); } BoundingBox bbox_travel = get_extents(travel); - AABBTree::BoundingBox bbox_travel_eigen{ bbox_travel.min, bbox_travel.max }; - int result = -1; + AABBTree::BoundingBox bbox_travel_eigen{bbox_travel.min, bbox_travel.max}; + int result = -1; bbox_travel.offset(SCALED_EPSILON); - AABBTreeIndirect::traverse(m_aabbtree_internal_islands, - [&bbox_travel_eigen](const AABBTree::Node &node) { - return bbox_travel_eigen.intersects(node.bbox); - }, + AABBTreeIndirect::traverse( + m_aabbtree_internal_islands, [&bbox_travel_eigen](const AABBTree::Node &node) { return bbox_travel_eigen.intersects(node.bbox); }, [&travel, &bbox_travel, &result, &islands = m_internal_islands](const AABBTree::Node &node) { assert(node.is_leaf()); assert(node.is_valid()); @@ -51,4 +110,10 @@ bool RetractWhenCrossingPerimeters::travel_inside_internal_regions(const Layer & return result != -1; } +bool RetractWhenCrossingPerimeters::travel_inside_internal_regions_no_wall_crossing(const Layer &layer, const Polyline &travel) +{ + if (!travel_inside_internal_regions(layer, travel)) return false; + return !travel_cross_perimeters(layer, travel); +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp index fb624d7..c93e8a7 100644 --- a/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/RetractWhenCrossingPerimeters.hpp @@ -4,6 +4,8 @@ #include #include "../AABBTreeIndirect.hpp" +#include "../AABBTreeLines.hpp" +#include "../Line.hpp" namespace Slic3r { @@ -15,16 +17,26 @@ class Polyline; class RetractWhenCrossingPerimeters { public: - bool travel_inside_internal_regions(const Layer &layer, const Polyline &travel); + bool travel_inside_internal_regions_no_wall_crossing(const Layer &layer, const Polyline &travel); + +private: + bool travel_cross_perimeters(const Layer &layer, const Polyline &travel); + bool travel_inside_internal_regions(const Layer &layer, const Polyline &travel); private: // Last object layer visited, for which a cache of internal islands was created. - const Layer *m_layer; + const Layer *m_layer; + // Search structure over internal islands. + BoundingBox m_internal_islands_bbox; + AABBTreeLines::LinesDistancer m_aabbtree_lines_distancer; + std::vector m_internal_islands_lines; + bool cross_perimeters_flag = false; + // Internal islands only, referencing data owned by m_layer->regions()->surfaces(). - std::vector m_internal_islands; + std::vector m_internal_islands; // Search structure over internal islands. using AABBTree = AABBTreeIndirect::Tree<2, coord_t>; - AABBTree m_aabbtree_internal_islands; + AABBTree m_aabbtree_internal_islands; }; } // namespace Slic3r diff --git a/src/libslic3r/GCode/TimelapsePosPicker.cpp b/src/libslic3r/GCode/TimelapsePosPicker.cpp index db54c98..51ab84a 100644 --- a/src/libslic3r/GCode/TimelapsePosPicker.cpp +++ b/src/libslic3r/GCode/TimelapsePosPicker.cpp @@ -139,6 +139,7 @@ namespace Slic3r { for (auto& obj : object_list) { for (auto& instance : obj->instances()) { auto instance_bbox = get_real_instance_bbox(instance); + bool higher_than_curr_layer = (layer->object() == obj) ? false : instance_bbox.max.z() > z_target; if(range_intersect(instance_bbox.min.z(), instance_bbox.max.z(), z_low, z_high)){ ExPolygon expoly; expoly.contour = { @@ -147,7 +148,7 @@ namespace Slic3r { {scale_(instance_bbox.max.x()), scale_(instance_bbox.max.y())}, {scale_(instance_bbox.min.x()), scale_(instance_bbox.max.y())} }; - expoly.contour = expand_object_projection(expoly.contour, by_object); + expoly.contour = expand_object_projection(expoly.contour, by_object, higher_than_curr_layer); ret.emplace_back(std::move(expoly)); } } @@ -249,11 +250,16 @@ namespace Slic3r { // expand the object expolygon by safe distance, scaled data - Polygon TimelapsePosPicker::expand_object_projection(const Polygon& poly, bool by_object) + Polygon TimelapsePosPicker::expand_object_projection(const Polygon &poly, bool by_object, bool higher_than_curr) { float radius = 0; - if (by_object) - radius = scale_(print->config().extruder_clearance_max_radius.value); + if (by_object) { + if (higher_than_curr) { + radius = scale_(print->config().extruder_clearance_max_radius.value); + }else{ + radius = scale_(print->config().extruder_clearance_max_radius.value / 2); + } + } else radius = scale_(print->config().extruder_clearance_max_radius.value / 2); @@ -557,7 +563,7 @@ namespace Slic3r { { max_p.x(),max_p.y() }, { min_p.x(),max_p.y() } }; - object_projections.emplace_back(expand_object_projection(obj_proj,by_object)); + object_projections.emplace_back(expand_object_projection(obj_proj, by_object)); } }; diff --git a/src/libslic3r/GCode/TimelapsePosPicker.hpp b/src/libslic3r/GCode/TimelapsePosPicker.hpp index cf7f9ea..c4930b3 100644 --- a/src/libslic3r/GCode/TimelapsePosPicker.hpp +++ b/src/libslic3r/GCode/TimelapsePosPicker.hpp @@ -45,7 +45,7 @@ namespace Slic3r { Polygons collect_limit_areas_for_rod(const std::vector& object_list, const PosPickCtx& ctx); - Polygon expand_object_projection(const Polygon& poly, bool by_object); + Polygon expand_object_projection(const Polygon &poly, bool by_object, bool higher_than_curr = true); BoundingBoxf3 expand_object_bbox(const BoundingBoxf3& bbox, bool by_object); Point pick_nearest_object_center(const Point& curr_pos, const std::vector& object_list); diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index d3cdcb4..c29492b 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -758,9 +758,18 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ for (int i = int(m_layer_tools.size()) - 2; i >= 0; -- i) m_layer_tools[i].wipe_tower_partitions = std::max(m_layer_tools[i + 1].wipe_tower_partitions, m_layer_tools[i].wipe_tower_partitions); + + int wrapping_layer_nums = config.wrapping_detection_layers; + for (size_t i = 0; i < wrapping_layer_nums; ++i) { + if (i >= m_layer_tools.size()) + break; + LayerTools < = m_layer_tools[i]; + lt.has_wipe_tower = config.enable_wrapping_detection; + } + //FIXME this is a hack to get the ball rolling. for (LayerTools < : m_layer_tools) - lt.has_wipe_tower = (lt.has_object && (config.timelapse_type == TimelapseType::tlSmooth || lt.wipe_tower_partitions > 0)) + lt.has_wipe_tower |= (lt.has_object && (config.timelapse_type == TimelapseType::tlSmooth || lt.wipe_tower_partitions > 0)) || lt.print_z < object_bottom_z + EPSILON; // Test for a raft, insert additional wipe tower layer to fill in the raft separation gap. @@ -818,7 +827,8 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ double last_wipe_tower_print_z = lt_next.print_z; while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower) if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height + EPSILON) { - m_layer_tools[j].has_wipe_tower = true; + if (!config.enable_wrapping_detection) + m_layer_tools[j].has_wipe_tower = true; last_wipe_tower_print_z = m_layer_tools[j].print_z; } } diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index baac9e5..9611dca 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1405,6 +1405,9 @@ WipeTower::ToolChangeResult WipeTower::construct_block_tcr(WipeTowerWriter &writ return result; } +// QDS +const double wrapping_wipe_tower_depth = 10; + // QDS const std::map WipeTower::min_depth_per_height = { {5.f,5.f}, {100.f, 20.f}, {250.f, 40.f}, {350.f, 60.f} @@ -1569,7 +1572,7 @@ TriangleMesh WipeTower::its_make_rib_brim(const Polygon& brim, float layer_heigh } -WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height) : +WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height, const std::vector& slice_used_filaments) : m_semm(config.single_extruder_multi_material.value), m_wipe_tower_pos(config.wipe_tower_x.get_at(plate_idx), config.wipe_tower_y.get_at(plate_idx)), m_wipe_tower_width(float(config.prime_tower_width)), @@ -1587,6 +1590,9 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi m_current_tool(initial_tool), //wipe_volumes(flush_matrix) m_enable_timelapse_print(config.timelapse_type.value == TimelapseType::tlSmooth), + m_enable_wrapping_detection(config.enable_wrapping_detection), + m_wrapping_detection_layers(config.wrapping_detection_layers.value && (config.wrapping_exclude_area.values.size() > 2)), + m_slice_used_filaments(slice_used_filaments.size()), m_filaments_change_length(config.filament_change_length.values), m_is_multi_extruder(config.nozzle_diameter.size() > 1), m_use_gap_wall(config.prime_tower_skip_points.value), @@ -2711,6 +2717,9 @@ void WipeTower::plan_tower() float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(m_wipe_tower_height); { + if (m_enable_wrapping_detection && max_depth < EPSILON) + max_depth = wrapping_wipe_tower_depth; + if (m_enable_timelapse_print && max_depth < EPSILON) max_depth = min_wipe_tower_depth; @@ -2762,6 +2771,9 @@ void WipeTower::plan_tower() for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth()); + if (m_enable_wrapping_detection && (layer_index < m_wrapping_detection_layers) && this_layer_depth < EPSILON) + this_layer_depth = wrapping_wipe_tower_depth; + if (m_enable_timelapse_print && this_layer_depth < EPSILON) this_layer_depth = min_wipe_tower_depth; @@ -2780,6 +2792,14 @@ void WipeTower::plan_tower() max_depth_for_all = m_plan[0].depth; } + if (m_enable_wrapping_detection) { + for (int i = m_wrapping_detection_layers - 1; i >= 0; i--) { + if (m_plan.size() <= m_wrapping_detection_layers && (m_plan[i].depth < wrapping_wipe_tower_depth)) { + m_plan[i].depth = wrapping_wipe_tower_depth; + } + } + } + if (m_enable_timelapse_print) { for (int i = int(m_plan.size()) - 1; i >= 0; i--) { m_plan[i].depth = max_depth_for_all; @@ -3185,7 +3205,7 @@ WipeTower::NozzleChangeResult WipeTower::nozzle_change_new(int old_filament_id, } } else { result.wipe_path.push_back(writer.pos_rotated()); - if (m_left_to_right) { + if (m_left_to_right) { result.wipe_path.push_back(Vec2f(0, writer.pos_rotated().y())); } else { result.wipe_path.push_back(Vec2f(m_wipe_tower_width, writer.pos_rotated().y())); @@ -3788,7 +3808,7 @@ void WipeTower::update_all_layer_depth(float wipe_tower_depth) if (m_wipe_tower_depth > 0) m_wipe_tower_depth += start_offset; - if (m_enable_timelapse_print) { + if (m_enable_wrapping_detection || m_enable_timelapse_print) { if (is_approx(m_wipe_tower_depth, 0.f)) m_wipe_tower_depth = wipe_tower_depth; for (WipeTowerInfo &plan_info : m_plan) { @@ -3997,6 +4017,13 @@ void WipeTower::plan_tower_new() // only for get m_extra_spacing { + if (m_enable_wrapping_detection && max_depth < EPSILON) { + max_depth = wrapping_wipe_tower_depth; + if (m_use_rib_wall) { + m_wipe_tower_width = max_depth; + } + } + if (m_enable_timelapse_print && max_depth < EPSILON) { max_depth = min_wipe_tower_depth; if (m_use_rib_wall) { m_wipe_tower_width = max_depth; } @@ -4174,6 +4201,7 @@ void WipeTower::generate_new(std::vectorm_cur_layer_id] = block.start_depth; }); @@ -4200,13 +4228,13 @@ void WipeTower::generate_new(std::vectordepth + m_perimeter_width; - if (is_new_mode && m_enable_timelapse_print) + if (is_new_mode && (m_enable_timelapse_print || m_enable_wrapping_detection)) wipe_tower_depth = m_wipe_tower_depth; box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)), m_wipe_tower_width, wipe_tower_depth); wt_box = align_perimeter(wt_box); @@ -4719,7 +4747,7 @@ bool WipeTower::is_valid_last_layer(int tool) const } float WipeTower::get_block_gap_width(int tool,bool is_nozzlechangle) { - //assert(m_block_infill_gap_width.count(m_filpar[tool].category));//The code contains logic that attempts to access non-existent blocks, + //assert(m_block_infill_gap_width.count(m_filpar[tool].category));//The code contains logic that attempts to access non-existent blocks, // such as in case of involving two extruders with only a single head and a single layer, // some code will attempt to access the block's nozzle_change_gap_width, even though the block does not exist. if (!m_block_infill_gap_width.count(m_filpar[tool].category)) { diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index eb22cca..62e6353 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -174,7 +174,7 @@ public: // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // wipe_area -- space available for one toolchange in mm // QDS: add partplate logic - WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height); + WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origin, size_t initial_tool, const float wipe_tower_height, const std::vector& slice_used_filaments); // Set the extruder properties. @@ -415,6 +415,9 @@ private: return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point } + int m_slice_used_filaments = 0; + int m_wrapping_detection_layers = 0; + bool m_enable_wrapping_detection = false; bool m_enable_timelapse_print = false; bool m_semm = true; // Are we using a single extruder multimaterial printer? Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. diff --git a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp index 5817b33..e7f76ce 100644 --- a/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp +++ b/src/libslic3r/Geometry/VoronoiUtilsCgal.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5514090..8ae0e06 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -788,6 +788,7 @@ void Model::convert_multipart_object(unsigned int max_extruders) ModelObject* object = new ModelObject(this); object->input_file = this->objects.front()->input_file; object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string(); + object->origin_translation = this->objects.front()->origin_translation; //FIXME copy the config etc? unsigned int extruder_counter = 0; diff --git a/src/libslic3r/OverhangDetector.cpp b/src/libslic3r/OverhangDetector.cpp index 27bb331..e271da7 100644 --- a/src/libslic3r/OverhangDetector.cpp +++ b/src/libslic3r/OverhangDetector.cpp @@ -257,10 +257,15 @@ namespace Slic3r { // map overhang speed to a range { auto it = std::upper_bound(non_uniform_degree_map.begin(), non_uniform_degree_map.end(), degree); - int high_idx = it - non_uniform_degree_map.begin(); - int low_idx = high_idx - 1; - double t = (degree - non_uniform_degree_map[low_idx]) / (non_uniform_degree_map[high_idx] - non_uniform_degree_map[low_idx]); - mapped_degree = low_idx * (1 - t) + t * high_idx; + if (it == non_uniform_degree_map.end()) { + mapped_degree = non_uniform_degree_map.size() - 1; + } + else { + int high_idx = it - non_uniform_degree_map.begin(); + int low_idx = high_idx - 1; + double t = (degree - non_uniform_degree_map[low_idx]) / (non_uniform_degree_map[high_idx] - non_uniform_degree_map[low_idx]); + mapped_degree = low_idx * (1 - t) + t * high_idx; + } } overhang_degree_arr.emplace_back(mapped_degree); } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index fb619d1..db4ed69 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -767,6 +767,21 @@ std::string Preset::get_printer_type(PresetBundle *preset_bundle) return ""; } +std::string Preset::get_printer_name(PresetBundle *preset_bundle) +{ + if (preset_bundle) { + auto config = &preset_bundle->printers.get_edited_preset().config; + std::string vendor_name; + for (auto vendor_profile : preset_bundle->vendors) { + for (auto vendor_model : vendor_profile.second.models) + if (vendor_model.name == config->opt_string("printer_model")) { + return vendor_model.name; + } + } + } + return ""; +} + std::string Preset::get_current_printer_type(PresetBundle *preset_bundle) { if (preset_bundle) { @@ -951,6 +966,7 @@ static std::vector s_Preset_print_options { "filter_out_gap_fill", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "small_perimeter_speed", "small_perimeter_threshold", "z_direction_outwall_speed_continuous", "vertical_shell_speed","detect_floating_vertical_shell", + "vertical_shell_speed","detect_floating_vertical_shell", "enable_wrapping_detection", // calib "print_flow_ratio", //Orca @@ -963,10 +979,10 @@ static std::vector s_Preset_print_options { ,"seal" }; -static std::vector s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "filament_type", +static std::vector s_Preset_filament_options{/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "volumetric_speed_coefficients", "filament_type", "filament_soluble", "filament_is_support", "filament_printable", "filament_scarf_seam_type", "filament_scarf_height", "filament_scarf_gap", "filament_scarf_length", - "filament_max_volumetric_speed", "impact_strength_z", "filament_ramming_volumetric_speed", + "filament_max_volumetric_speed", "impact_strength_z", "filament_ramming_volumetric_speed", "filament_adaptive_volumetric_speed", "filament_flow_ratio", "filament_density", "filament_adhesiveness_category", "filament_cost", "filament_minimal_purge_on_wipe_tower", "nozzle_temperature", "nozzle_temperature_initial_layer", // QDS @@ -992,7 +1008,7 @@ static std::vector s_Preset_filament_options {/*"filament_colour", "filament_extruder_variant", //OrcaSlicer "enable_pressure_advance", "pressure_advance", "chamber_temperatures","filament_notes", - "filament_long_retractions_when_cut","filament_retraction_distances_when_cut","filament_shrink", + "filament_long_retractions_when_cut","filament_retraction_distances_when_cut","filament_shrink", "filament_velocity_adaptation_factor", //QDS filament change length while the extruder color "filament_change_length","filament_prime_volume","filament_flush_volumetric_speed","filament_flush_temp", "long_retractions_when_ec", "retraction_distances_when_ec", @@ -1015,14 +1031,14 @@ static std::vector s_Preset_machine_limits_options { static std::vector s_Preset_printer_options { "printer_technology", "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", - "single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode","printing_by_object_gcode","before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", + "single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode","printing_by_object_gcode","before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printable_height", "extruder_printable_height", "extruder_clearance_dist_to_rod", "extruder_clearance_max_radius","extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", "nozzle_height", "master_extruder_id", "default_print_profile", "inherits", "silent_mode", // QDS - "scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode", + "scan_first_layer", "wrapping_detection_layers", "wrapping_exclude_area", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode", "nozzle_type","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types","support_chamber_temp_control","support_air_filtration","printer_structure","thumbnail_size", //w12 "thumbnails_formats", @@ -2603,6 +2619,7 @@ bool PresetCollection::delete_preset(const std::string& name) } //QDS: add lock logic for sync preset in background lock(); + set_printer_hold_alias(it->alias, *it, true); it = m_presets.erase(it); if (std::distance(m_presets.begin(), it) < m_idx_selected) --m_idx_selected; @@ -2761,7 +2778,7 @@ size_t PresetCollection::first_visible_idx() const size_t first_visible = -1; size_t idx = m_default_suppressed ? m_num_default_presets : 0; for (; idx < m_presets.size(); ++ idx) - if (m_presets[idx].is_visible) { + if (m_presets[idx].is_visible && m_presets[idx].get_printer_id() == "QDT") { if (first_visible == -1) first_visible = idx; if (m_type != Preset::TYPE_FILAMENT) @@ -2773,8 +2790,12 @@ size_t PresetCollection::first_visible_idx() const } } } - if (first_visible == -1) - first_visible = 0; + if (first_visible == -1) { + if (m_presets.size() > 1 && m_default_suppressed) + first_visible = m_presets.size() == m_num_default_presets ? 0 : m_num_default_presets; + else + first_visible = 0; + } return first_visible; } @@ -2907,7 +2928,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt) { //QDS: add bed_exclude_area - if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers"|| opt_key == "thumbnail_size") { + if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers"|| opt_key == "thumbnail_size" || opt_key == "wrapping_exclude_area") { // Scalar variable, or a vector variable, which is independent from number of extruders, // thus the vector is presented to the user as a single input. diff.emplace_back(opt_key); @@ -3189,22 +3210,43 @@ void PresetCollection::set_custom_preset_alias(Preset &preset) } } -void PresetCollection::set_printer_hold_alias(const std::string &alias, Preset &preset) +void PresetCollection::set_printer_hold_alias(const std::string &alias, Preset &preset, bool remove) { auto compatible_printers = dynamic_cast(preset.config.option("compatible_printers")); if (compatible_printers == nullptr) return; for (const std::string &printer_name : compatible_printers->values) { auto printer_iter = m_printer_hold_alias.find(printer_name); + bool insert_success = false, remove_success = false; if (m_printer_hold_alias.end() == printer_iter) { - m_printer_hold_alias[printer_name].insert(alias); - } else { - auto alias_iter = m_printer_hold_alias[printer_name].find(alias); - if (m_printer_hold_alias[printer_name].end() == alias_iter) { + if (!remove) { + insert_success = true; m_printer_hold_alias[printer_name].insert(alias); + } + } else { + auto &printer_filament_alias = m_printer_hold_alias[printer_name]; + auto alias_iter = printer_filament_alias.find(alias); + if (printer_filament_alias.end() == alias_iter) { + if (!remove) { + insert_success = true; + printer_filament_alias.insert(alias); + } } else { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << printer_name << "already has alias: " << alias << " and the preset name: " << preset.name; + if (remove) { + if (preset.inherits() == "") { + remove_success = true; + printer_filament_alias.erase(alias); + } + if (auto alias_iter = m_map_alias_to_profile_name.find(alias); alias_iter != m_map_alias_to_profile_name.end()) { + auto& presets = alias_iter->second; + auto new_end = std::remove(presets.begin(), presets.end(), preset.name); + presets.erase(new_end, presets.end()); + if (presets.empty()) { m_map_alias_to_profile_name.erase(alias); } + } + } } } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << " preset name : " << preset.name << " remove action: " << remove << " insert success: " + << insert_success << " remove success: " << remove_success << " alias: " << alias; } } diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index a781dab..b1a41b3 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -329,9 +329,11 @@ public: // special for upport G and Support W std::string get_filament_type(std::string &display_filament_type); std::string get_printer_type(PresetBundle *preset_bundle); // get edited preset type + std::string get_printer_name(PresetBundle *preset_bundle); std::string get_current_printer_type(PresetBundle *preset_bundle); // get current preset type static void get_extruder_names_and_keysets(Type type, std::string& extruder_id_name, std::string& extruder_variant_name, std::set** p_key_set1, std::set** p_key_set2); + std::string get_printer_id() const { return vendor ? vendor->id : ""; } bool has_lidar(PresetBundle *preset_bundle); bool is_custom_defined(); @@ -578,7 +580,7 @@ public: const std::string& get_preset_name_by_alias(const std::string& alias) const; const std::string* get_preset_name_renamed(const std::string &old_name) const; bool is_alias_exist(const std::string &alias, Preset* preset = nullptr); - void set_printer_hold_alias(const std::string &alias, Preset &preset); + void set_printer_hold_alias(const std::string &alias, Preset &preset, bool remove = false); // used to update preset_choice from Tab const std::deque& get_presets() const { return m_presets; } diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index f3d3845..31e8a1d 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -60,6 +60,183 @@ const char *PresetBundle::QDT_DEFAULT_PRINTER_MODEL = "X-Plus 4"; const char *PresetBundle::QDT_DEFAULT_PRINTER_VARIANT = "0.4"; const char *PresetBundle::QDT_DEFAULT_FILAMENT = "QIDI PLA Rapido"; +DynamicPrintConfig PresetBundle::construct_full_config( + Preset& in_printer_preset, + Preset& in_print_preset, + const DynamicPrintConfig& project_config, + std::vector& in_filament_presets, + bool apply_extruder, + std::optional> filament_maps_new) +{ + DynamicPrintConfig &printer_config = in_printer_preset.config; + DynamicPrintConfig &print_config = in_print_preset.config; + + DynamicPrintConfig out; + out.apply(FullPrintConfig::defaults()); + out.apply(printer_config); + out.apply(print_config); + out.apply(project_config); + out.apply(in_filament_presets[0].config); + + size_t num_filaments = in_filament_presets.size(); + + std::vector filament_maps = out.option("filament_map")->values; + if (filament_maps_new.has_value()) + filament_maps = *filament_maps_new; + // in some middle state, they may be different + if (filament_maps.size() != num_filaments) { + filament_maps.resize(num_filaments, 1); + } + + auto *extruder_diameter = dynamic_cast(out.option("nozzle_diameter")); + // Collect the "compatible_printers_condition" and "inherits" values over all presets (print, filaments, printers) into a single vector. + std::vector compatible_printers_condition; + std::vector compatible_prints_condition; + std::vector inherits; + std::vector filament_ids; + std::vector print_compatible_printers; + // QDS: add logic for settings check between different system presets + std::vector different_settings; + std::string different_print_settings, different_printer_settings; + compatible_printers_condition.emplace_back(in_print_preset.compatible_printers_condition()); + + const ConfigOptionStrings *compatible_printers = print_config.option("compatible_printers", false); + if (compatible_printers) print_compatible_printers = compatible_printers->values; + // QDS: add logic for settings check between different system presets + std::string print_inherits = in_print_preset.inherits(); + inherits.emplace_back(print_inherits); + + // QDS: update printer config related with variants + if (apply_extruder) { + out.update_values_to_printer_extruders(out, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant"); + out.update_values_to_printer_extruders(out, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2); + // update print config related with variants + out.update_values_to_printer_extruders(out, print_options_with_variant, "print_extruder_id", "print_extruder_variant"); + } + + if (num_filaments <= 1) { + // QDS: update filament config related with variants + DynamicPrintConfig filament_config = in_filament_presets[0].config; + if (apply_extruder) filament_config.update_values_to_printer_extruders(out, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[0]); + out.apply(filament_config); + compatible_printers_condition.emplace_back(in_filament_presets[0].compatible_printers_condition()); + compatible_prints_condition.emplace_back(in_filament_presets[0].compatible_prints_condition()); + std::string filament_inherits = in_filament_presets[0].inherits(); + inherits.emplace_back(filament_inherits); + filament_ids.emplace_back(in_filament_presets[0].filament_id); + + std::vector &filament_self_indice = out.option("filament_self_index", true)->values; + int index_size = out.option("filament_extruder_variant")->size(); + filament_self_indice.resize(index_size, 1); + } else { + std::vector filament_configs; + std::vector filament_presets; + for (const Preset & preset : in_filament_presets) { + filament_presets.emplace_back(&preset); + filament_configs.emplace_back(&(preset.config)); + } + + std::vector filament_temp_configs; + filament_temp_configs.resize(num_filaments); + for (size_t i = 0; i < num_filaments; ++i) { + filament_temp_configs[i] = *(filament_configs[i]); + if (apply_extruder) + filament_temp_configs[i].update_values_to_printer_extruders(out, filament_options_with_variant, "", "filament_extruder_variant", 1, filament_maps[i]); + } + + // loop through options and apply them to the resulting config. + std::vector filament_variant_count(num_filaments, 1); + for (const t_config_option_key &key : in_filament_presets[0].config.keys()) { + if (key == "compatible_prints" || key == "compatible_printers") continue; + // Get a destination option. + ConfigOption *opt_dst = out.option(key, false); + if (opt_dst->is_scalar()) { + // Get an option, do not create if it does not exist. + const ConfigOption *opt_src = filament_temp_configs.front().option(key); + if (opt_src != nullptr) opt_dst->set(opt_src); + } else { + // QDS + ConfigOptionVectorBase *opt_vec_dst = static_cast(opt_dst); + { + if (apply_extruder) { + std::vector filament_opts(num_filaments, nullptr); + // Setting a vector value from all filament_configs. + for (size_t i = 0; i < filament_opts.size(); ++i) filament_opts[i] = filament_temp_configs[i].option(key); + opt_vec_dst->set(filament_opts); + } else { + for (size_t i = 0; i < num_filaments; ++i) { + const ConfigOptionVectorBase *filament_option = static_cast(filament_temp_configs[i].option(key)); + if (i == 0) + opt_vec_dst->set(filament_option); + else + opt_vec_dst->append(filament_option); + + if (key == "filament_extruder_variant") filament_variant_count[i] = filament_option->size(); + } + } + } + } + } + + if (!apply_extruder) { + // append filament_self_index + std::vector &filament_self_indice = out.option("filament_self_index", true)->values; + int index_size = out.option("filament_extruder_variant")->size(); + filament_self_indice.resize(index_size, 1); + int k = 0; + for (size_t i = 0; i < num_filaments; i++) { + for (size_t j = 0; j < filament_variant_count[i]; j++) { filament_self_indice[k++] = i + 1; } + } + } + } + + // These value types clash between the print and filament profiles. They should be renamed. + out.erase("compatible_prints"); + out.erase("compatible_prints_condition"); + out.erase("compatible_printers"); + out.erase("compatible_printers_condition"); + out.erase("inherits"); + // QDS: add logic for settings check between different system presets + out.erase("different_settings_to_system"); + + static const char *keys[] = {"support_filament", "support_interface_filament"}; + for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) { + std::string key = std::string(keys[i]); + auto *opt = dynamic_cast(out.option(key, false)); + assert(opt != nullptr); + opt->value = boost::algorithm::clamp(opt->value, 0, int(num_filaments)); + } + + std::vector filamnet_preset_names; + for (auto preset : in_filament_presets) { + filamnet_preset_names.emplace_back(preset.name); + } + out.option("print_settings_id", true)->value = in_print_preset.name; + out.option("filament_settings_id", true)->values = filamnet_preset_names; + out.option("printer_settings_id", true)->value = in_printer_preset.name; + out.option("filament_ids", true)->values = filament_ids; + out.option("filament_map", true)->values = filament_maps; + + auto add_if_some_non_empty = [&out](std::vector &&values, const std::string &key) { + bool nonempty = false; + for (const std::string &v : values) + if (!v.empty()) { + nonempty = true; + break; + } + if (nonempty) out.set_key_value(key, new ConfigOptionStrings(std::move(values))); + }; + add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_machine_expression_group"); + add_if_some_non_empty(std::move(compatible_prints_condition), "compatible_process_expression_group"); + add_if_some_non_empty(std::move(inherits), "inherits_group"); + // QDS: add logic for settings check between different system presets + //add_if_some_non_empty(std::move(different_settings), "different_settings_to_system"); + add_if_some_non_empty(std::move(print_compatible_printers), "print_compatible_printers"); + + out.option("printer_technology", true)->value = ptFFF; + return out; +} + PresetBundle::PresetBundle() : prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast(FullPrintConfig::defaults())) , filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast(FullPrintConfig::defaults()), "Default Filament") @@ -899,7 +1076,7 @@ bool PresetBundle::import_json_presets(PresetsConfigSubstitutions & s << ", which were removed"; if (!config_substitutions.empty()) substitutions.push_back({name, collection->type(), PresetConfigSubstitutions::Source::UserFile, file, std::move(config_substitutions)}); - + collection->set_custom_preset_alias(preset); preset.save(inherit_preset ? &inherit_preset->config : nullptr); result.push_back(file); } catch (const std::ifstream::failure &err) { @@ -1026,7 +1203,7 @@ void PresetBundle::update_system_preset_setting_ids(std::map gcodes_key_set = {"filament_end_gcode", "filament_start_gcode", "change_filament_gcode", "layer_change_gcode", "machine_end_gcode", "machine_pause_gcode", "machine_start_gcode", - "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode"}; + "template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode"}; int PresetBundle::validate_presets(const std::string &file_name, DynamicPrintConfig& config, std::set& different_gcodes) { bool validated = false; @@ -4442,7 +4619,7 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam f_multiplier.resize(nozzle_nums, 1.f); } - if (num_filaments != old_number_of_filaments) { + if ( (num_filaments * num_filaments) != size_t(old_matrix.size() / old_nozzle_nums) ) { // First verify if purging volumes presets for each extruder matches number of extruders std::vector& filaments = this->project_config.option("flush_volumes_vector")->values; while (filaments.size() < 2* num_filaments) { diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 4cd282c..7cbe914 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -71,6 +71,12 @@ struct FilamentBaseInfo class PresetBundle { public: + static DynamicPrintConfig construct_full_config(Preset &in_printer_preset, + Preset &in_print_preset, + const DynamicPrintConfig &project_config, + std::vector &in_filament_presets, + bool apply_extruder, + std::optional> filament_maps_new); PresetBundle(); PresetBundle(const PresetBundle &rhs); PresetBundle& operator=(const PresetBundle &rhs); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e3f43f8..c5dd345 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -35,6 +35,7 @@ #include "nlohmann/json.hpp" #include "GCode/ConflictChecker.hpp" +#include "ParameterUtils.hpp" #include @@ -126,6 +127,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "filament_colour", "default_filament_colour", "filament_diameter", + "volumetric_speed_coefficients", "filament_density", "filament_cost", "initial_layer_acceleration", @@ -142,6 +144,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "gcode_add_line_number", "layer_change_gcode", "time_lapse_gcode", + "wrapping_detection_gcode", "fan_min_speed", "fan_max_speed", "printable_height", @@ -205,6 +208,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "filament_notes", "process_notes", "printer_notes", + "filament_velocity_adaptation_factor", //w13 "additional_cooling_fan_speed_unseal", //w14 @@ -273,6 +277,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "nozzle_temperature_initial_layer" || opt_key == "filament_minimal_purge_on_wipe_tower" || opt_key == "filament_max_volumetric_speed" + || opt_key == "filament_adaptive_volumetric_speed" || opt_key == "filament_ramming_volumetric_speed" || opt_key == "gcode_flavor" || opt_key == "single_extruder_multi_material" @@ -286,6 +291,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "hot_plate_temp" || opt_key == "textured_plate_temp" || opt_key == "enable_prime_tower" + || opt_key == "enable_wrapping_detection" || opt_key == "prime_tower_enable_framework" || opt_key == "prime_tower_width" || opt_key == "prime_tower_max_speed" @@ -924,47 +930,40 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, } } - std::map map_model_object_to_convex_hull; - // sequential_print_horizontal_clearance_valid + Pointfs wrapping_detection_area = print_config.wrapping_exclude_area.values; + Polygon wrapping_poly; + for (size_t i = 0; i < wrapping_detection_area.size(); ++i) { + auto pt = wrapping_detection_area[i]; + wrapping_poly.points.emplace_back(scale_(pt.x() + print_origin.x()), scale_(pt.y() + print_origin.y())); + } + + std::map map_model_volume_to_convex_hull; Polygons convex_hulls_other; - for (int k = 0; k < print_instances_ordered.size(); k++) - { - auto& inst = print_instances_ordered[k]; - auto it_convex_hull = map_model_object_to_convex_hull.find(inst); - // Get convex hull of all printable volumes assigned to this print object. - const ModelInstance* model_instance0 = inst->model_instance; - if (it_convex_hull == map_model_object_to_convex_hull.end()) { - // Calculate the convex hull of a printable object. - auto convex_hull0 = inst->print_object->model_object()->convex_hull_2d( - Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())); + for (auto& inst : print_instances_ordered) { + for (const ModelVolume *v : inst->print_object->model_object()->volumes) { + if (!v->is_model_part()) continue; + auto it_convex_hull = map_model_volume_to_convex_hull.find(v); + if (it_convex_hull == map_model_volume_to_convex_hull.end()) { + auto volume_hull = v->get_convex_hull_2d(Geometry::assemble_transform(Vec3d::Zero(), inst->model_instance->get_rotation(), + inst->model_instance->get_scaling_factor(), inst->model_instance->get_mirror())); + volume_hull.translate(inst->shift - inst->print_object->center_offset()); - double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), inst->model_instance->get_rotation()); - if (std::abs(z_diff) > EPSILON) - convex_hull0.rotate(z_diff); - - // instance.shift is a position of a centered object, while model object may not be centered. - // Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset. - convex_hull0.translate(inst->shift - inst->print_object->center_offset()); - - it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, inst, convex_hull0); - } - Polygon& convex_hull = it_convex_hull->second; - Polygons convex_hulls_temp; - convex_hulls_temp.push_back(convex_hull); - /*if (!intersection(convex_hulls_other, convex_hulls_temp).empty()) { - if (warning) { - warning->string = inst->model_instance->get_object()->name + L(" is too close to others, there may be collisions when printing.") + "\n"; - warning->object = inst->model_instance->get_object(); + it_convex_hull = map_model_volume_to_convex_hull.emplace_hint(it_convex_hull, v, volume_hull); } - }*/ - if (!intersection(exclude_polys, convex_hull).empty()) { - return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n", inst->model_instance->get_object()}; - /*if (warning) { - warning->string = inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n"; - warning->object = inst->model_instance->get_object(); - }*/ + Polygon &convex_hull = it_convex_hull->second; + Polygons convex_hulls_temp; + convex_hulls_temp.push_back(convex_hull); + if (!intersection(exclude_polys, convex_hull).empty()) { + return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n", + inst->model_instance->get_object()}; + } + + if (print_config.enable_wrapping_detection.value && !intersection(wrapping_poly, convex_hull).empty()) { + return {inst->model_instance->get_object()->name + L(" is too close to clumping detection area, there may be collisions when printing.") + "\n", + inst->model_instance->get_object()}; + } + convex_hulls_other.emplace_back(convex_hull); } - convex_hulls_other.emplace_back(convex_hull); } //QDS: add the wipe tower check logic @@ -996,7 +995,9 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, convex_hulls_temp.push_back(wipe_tower_convex_hull); } else { //here, wipe_tower_polygon is not always convex. - Polygon wipe_tower_polygon = print.wipe_tower_data().wipe_tower_mesh_data->bottom; + Polygon wipe_tower_polygon; + if (print.wipe_tower_data().wipe_tower_mesh_data) + wipe_tower_polygon = print.wipe_tower_data().wipe_tower_mesh_data->bottom; wipe_tower_polygon.translate(Point(scale_(x), scale_(y))); convex_hulls_temp.push_back(wipe_tower_polygon); } @@ -1012,7 +1013,9 @@ static StringObjectException layered_print_cleareance_valid(const Print &print, }*/ return {L("Prime Tower") + L(" is too close to exclusion area, and collisions will be caused.\n")}; } - + if (print_config.enable_wrapping_detection.value && !intersection({wrapping_poly}, convex_hulls_temp).empty()) { + return {L("Prime Tower") + L(" is too close to clumping detection area, and collisions will be caused.\n")}; + } return {}; } @@ -1083,6 +1086,53 @@ int Print::get_compatible_filament_type(const std::set& filament_types) StringObjectException Print::check_multi_filament_valid(const Print& print) { auto print_config = print.config(); + if(print_config.print_sequence == PrintSequence::ByObject) {// use ByObject valid under ByObject print sequence + std::set Compatibility_each_obj; + bool enable_mix_printing = !print.need_check_multi_filaments_compatibility(); + + for (const auto &objectID_t : print.print_object_ids()) { + std::set obj_used_extruder_ids; + auto print_object = print.get_object(objectID_t);// current object + if (print_object){ + auto object_extruders_t = print_object->object_extruders(); // object used extruder + for (int extruder : object_extruders_t) { + assert(extruder > 0); + obj_used_extruder_ids.insert(extruder); + } + } + + if (print_object->has_support_material()) { // extruder used by supports + auto num_extruders = (unsigned int) print_config.filament_diameter.size(); + assert(print_object->config().support_filament >= 0); + if (print_object->config().support_filament >= 1 && (unsigned int)print_object->config().support_filament < num_extruders + 1) + obj_used_extruder_ids.insert((unsigned int) print_object->config().support_filament - 1);//0-based extruder id + assert(print_object->config().support_interface_filament >= 0); + if (print_object->config().support_interface_filament >= 1 && (unsigned int)print_object->config().support_interface_filament < num_extruders + 1) + obj_used_extruder_ids.insert((unsigned int) print_object->config().support_interface_filament - 1); + } + std::vector filament_types; + filament_types.reserve(obj_used_extruder_ids.size()); + for (const auto &extruder_idx : obj_used_extruder_ids) filament_types.push_back(print_config.filament_type.get_at(extruder_idx)); + + auto compatibility = check_multi_filaments_compatibility(filament_types);// check for each object + Compatibility_each_obj.insert(compatibility); + } + StringObjectException ret; + std::string hypertext = "filament_mix_print"; + if (Compatibility_each_obj.count(FilamentCompatibilityType::HighLowMixed)){// at least one object has HighLowMixed + if (enable_mix_printing) { + ret.string = L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage."); + ret.is_warning = true; + ret.hypetext = hypertext; + } else + ret.string = L("Printing high-temp and low-temp filaments together may cause nozzle clogging or printer damage. If you still want to print, you can enable the option in Preferences."); + }else if (Compatibility_each_obj.count(FilamentCompatibilityType::LowMidMixed) || Compatibility_each_obj.count(FilamentCompatibilityType::HighMidMixed)){// at least one object has other Mixed + ret.is_warning = true; + ret.hypetext = hypertext; + ret.string = L("Printing different-temp filaments together may cause nozzle clogging or printer damage."); + } + return ret; + } std::vector extruders = print.extruders(); std::vector filament_types; filament_types.reserve(extruders.size()); @@ -1122,7 +1172,7 @@ StringObjectException Print::check_multi_filament_valid(const Print& print) } // Precondition: Print::validate() requires the Print::apply() to be called its invocation. -//QDS: refine seq-print validation logic +//QDS: refine seq-print validation logic.....FIXME:StringObjectException *warning can only contain one warning, but there might be many warnings, need a vector StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector>* height_polygons) const { std::vector extruders = this->extruders(); @@ -1133,23 +1183,30 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* if (extruders.empty()) return { L("No extrusions under current settings.") }; - if (extruders.size() > 1 && m_config.print_sequence != PrintSequence::ByObject) { + if (extruders.size() > 1) { auto ret = check_multi_filament_valid(*this); if (!ret.string.empty()) { ret.type = STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP; if (ret.is_warning && warning != nullptr) { *warning = ret; - return {}; - } - return ret; + //return {}; + }else + return ret; } } - if (m_config.print_sequence == PrintSequence::ByObject) { + if (m_config.print_sequence == PrintSequence::ByObject && m_objects.size() > 1) { if (m_config.timelapse_type == TimelapseType::tlSmooth) return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")}; + if (m_config.enable_wrapping_detection) { + StringObjectException clumping_detection_setting_err; + clumping_detection_setting_err.string = L("Clumping detection is not supported when \"by object\" sequence is enabled."); + clumping_detection_setting_err.opt_key = "enable_wrapping_detection"; + return clumping_detection_setting_err; + } + //QDS: refine seq-print validation logic auto ret = sequential_print_clearance_valid(*this, collison_polygons, height_polygons); if (!ret.string.empty()) { @@ -1166,6 +1223,17 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* } } + if (m_config.enable_prime_tower) { + } else { + if (m_config.enable_wrapping_detection && warning!=nullptr) { + StringObjectException warningtemp; + warningtemp.string = L("Prime tower is required for clumping detection; otherwise, there may be flaws on the model."); + warningtemp.opt_key = "enable_prime_tower"; + warningtemp.is_warning = true; + *warning = warningtemp; + } + } + if (m_config.spiral_mode) { size_t total_copies_count = 0; for (const PrintObject* object : m_objects) @@ -1385,8 +1453,10 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* bool has_enforcers = mv->is_support_enforcer() || (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER)); if (has_enforcers) { - warning->string = L("Support enforcers are used but support is not enabled. Please enable support."); - warning->object = object; + StringObjectException warningtemp; + warningtemp.string = L("Support enforcers are used but support is not enabled. Please enable support."); + warningtemp.object = object; + *warning = warningtemp; break; } } @@ -1750,6 +1820,8 @@ void Print::process(std::unordered_map* slice_time, bool } //if (!object1->config().equals(object2->config())) // return false; + if (model_obj1->layer_height_profile.get() != model_obj2->layer_height_profile.get()) + return false; if (model_obj1->config.get() != model_obj2->config.get()) return false; return true; @@ -2665,6 +2737,9 @@ size_t Print::get_extruder_id(unsigned int filament_id) const bool Print::has_wipe_tower() const { if (m_config.enable_prime_tower.value == true) { + if (m_config.enable_wrapping_detection.value && m_config.wrapping_exclude_area.values.size() > 2) + return true; + if (enable_timelapse_print()) return true; @@ -2683,31 +2758,56 @@ const WipeTowerData& Print::wipe_tower_data(size_t filaments_cnt) const } if (max_height < EPSILON) return m_wipe_tower_data; + double layer_height = 0.08f; // hard code layer height + layer_height = m_objects.front()->config().layer_height.value; + + auto timelapse_type = config().option>("timelapse_type"); + bool need_wipe_tower = (timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false) | m_config.prime_tower_rib_wall.value; + double extra_spacing = config().option("prime_tower_infill_gap")->getFloat() / 100.; + double rib_width = config().option("prime_tower_rib_width")->getFloat(); + + double filament_change_volume = 0.; + { + std::vector filament_change_lengths; + auto filament_change_lengths_opt = config().option("filament_change_length"); + if (filament_change_lengths_opt) filament_change_lengths = filament_change_lengths_opt->values; + double length = filament_change_lengths.empty() ? 0 : *std::max_element(filament_change_lengths.begin(), filament_change_lengths.end()); + double diameter = 1.75; + std::vector diameters; + auto filament_diameter_opt = config().option("filament_diameter"); + if (filament_diameter_opt) diameters = filament_diameter_opt->values; + diameter = diameters.empty() ? diameter : *std::max_element(diameters.begin(), diameters.end()); + filament_change_volume = length * PI * diameter * diameter / 4.; + } + + if (! is_step_done(psWipeTower) && filaments_cnt !=0) { std::vector filament_wipe_volume = m_config.filament_prime_volume.values; double wipe_volume = get_max_element(filament_wipe_volume); - if (m_config.prime_tower_rib_wall.value) { - double layer_height = 0.08f; // hard code layer height - layer_height = m_objects.front()->config().layer_height.value; - int filament_depth_count = m_config.nozzle_diameter.values.size() == 2 ? filaments_cnt : filaments_cnt - 1; - if (filaments_cnt == 1 && enable_timelapse_print()) - filament_depth_count = 1; - double depth = std::sqrt(wipe_volume * filament_depth_count / layer_height); + int filament_depth_count = m_config.nozzle_diameter.values.size() == 2 ? filaments_cnt : filaments_cnt - 1; + if (filaments_cnt == 1 && enable_timelapse_print()) filament_depth_count = 1; + double volume = wipe_volume * filament_depth_count; + if (m_config.nozzle_diameter.values.size() == 2) volume += filament_change_volume * (int) (filaments_cnt / 2); - float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height); - depth = std::max((double) min_wipe_tower_depth, depth); - const_cast(this)->m_wipe_tower_data.depth = depth; - const_cast(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width; + if (m_config.prime_tower_rib_wall.value) { + double depth = std::sqrt(volume / layer_height * extra_spacing); + if (need_wipe_tower || filaments_cnt > 1) { + float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height); + depth = std::max((double) min_wipe_tower_depth, depth); + depth += rib_width / std::sqrt(2) + config().prime_tower_extra_rib_length.value; + const_cast(this)->m_wipe_tower_data.depth = depth; + const_cast(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width; + } } else { // QDS double width = m_config.prime_tower_width; - double layer_height = 0.2; // hard code layer height - if (filaments_cnt == 1 && enable_timelapse_print()) { - const_cast(this)->m_wipe_tower_data.depth = wipe_volume / (layer_height * width); - } else { - const_cast(this)->m_wipe_tower_data.depth = wipe_volume * (filaments_cnt - 1) / (layer_height * width); + double depth = volume / (layer_height * width) * extra_spacing; + if (need_wipe_tower || m_wipe_tower_data.depth > EPSILON) { + float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height); + depth = std::max((double) min_wipe_tower_depth, depth); } + const_cast(this)->m_wipe_tower_data.depth = depth; const_cast(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width; } if (m_config.prime_tower_brim_width < 0) const_cast(this)->m_wipe_tower_data.brim_width = WipeTower::get_auto_brim_by_height(max_height); @@ -2777,7 +2877,7 @@ void Print::_make_wipe_tower() // Initialize the wipe tower. // QDS: in QDT machine, wipe tower is only use to prime extruder. So just use a global wipe volume. WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_wipe_tower_data.tool_ordering.first_extruder(), - m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z); + m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z, m_wipe_tower_data.tool_ordering.all_extruders()); wipe_tower.set_has_tpu_filament(this->has_tpu_filament()); wipe_tower.set_filament_map(this->get_filament_maps()); // Set the extruder & material properties at the wipe tower object. @@ -2848,7 +2948,7 @@ void Print::_make_wipe_tower() layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this); // if enable timelapse, slice all layer - if (enable_timelapse_print()) { + if (m_config.enable_wrapping_detection || enable_timelapse_print()) { if (layer_tools.wipe_tower_partitions == 0) wipe_tower.set_last_layer_extruder_fill(false); continue; } @@ -4234,10 +4334,12 @@ Polygon PrintInstance::get_convex_hull_2d() { Polygon poly = print_object->model_object()->convex_hull_2d(model_instance->get_matrix()); //y50 float distance_tolerance = 0.1; - while(poly.size() > 200){ + poly.douglas_peucker(distance_tolerance); + while (poly.size() > 200) { poly.douglas_peucker(distance_tolerance); - distance_tolerance*=2; + distance_tolerance *= 2; } + return poly; } diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 38935ff..5c772a7 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1142,6 +1142,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ std::vector scarf_seam_type = new_full_config.option("filament_scarf_seam_type")->values; auto check_object_scarf_seam_type = [](const std::vector scarf_seam_type, const ModelObject *mo) { //get filament scarf seam type + if (!mo->printable) + return false; //check volumes for (ModelVolume *mv : mo->volumes) { std::vector volume_extruders = mv->get_extruders(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f71bf5b..c1d7f8c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1586,7 +1586,7 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloatsOrPercentsNullable{FloatOrPercent(50, true)}); def = this->add("small_perimeter_threshold", coFloats); - def->label = L("Small perimter threshold"); + def->label = L("Small perimeter threshold"); def->category = L("Speed"); def->tooltip = L("This sets the threshold for small perimeter length. Default threshold is 0mm"); def->sidetext = L("mm"); @@ -1976,6 +1976,19 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->set_default_value(new ConfigOptionFloats { 1.75 }); + def = this->add("filament_adaptive_volumetric_speed", coBools); + def->label = L("Adaptive volumetric speed"); + def->tooltip = L("When enabled, the extrusion flow is limited by the smaller of " + "the fitted value (calculated from line width and layer height) and the user-defined maximum flow." + " When disabled, only the user-defined maximum flow is applied."); + def->mode = comAdvanced; + def->nullable = true; + def->set_default_value(new ConfigOptionBoolsNullable {false}); + + def = this->add("volumetric_speed_coefficients", coStrings); + def->label = L("Max volumetric speed multinomial coefficients"); + def->set_default_value(new ConfigOptionStrings{""}); + def = this->add("filament_shrink", coPercents); def->label = L("Shrinkage"); // xgettext:no-c-format, no-boost-format @@ -1989,6 +2002,15 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionPercents{ 100 }); + def = this->add("filament_velocity_adaptation_factor", coFloats); + def->label = L("Velocity Adaptation Factor"); + def->min = 0; + def->tooltip = L("This parameter reflects the speed at which a material transitions from one state to another. " + "It, along with the smooth coefficient, determines the final length of the transition zone. " + "A larger value: requires a shorter transition zone. " + "A smaller value: requires a longer transition zone to avoid flow instability."); + def->set_default_value(new ConfigOptionFloats{1.0}); + def = this->add("filament_adhesiveness_category", coInts); def->label = L("Adhesiveness Category"); def->tooltip = L("Filament category"); @@ -2072,7 +2094,7 @@ void PrintConfigDef::init_fff_params() def->ratio_over = "layer_height"; def->sidetext = L("mm/%"); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloatsOrPercents{FloatOrPercent( 0, 10)}); + def->set_default_value(new ConfigOptionFloatsOrPercents{FloatOrPercent(10, true)}); def = this->add("filament_scarf_gap", coFloatsOrPercents); def->label = L("Scarf slope gap"); @@ -2630,6 +2652,27 @@ void PrintConfigDef::init_fff_params() def->mode = comDevelop; def->set_default_value(new ConfigOptionBool(false)); + //QDS + def = this->add("enable_wrapping_detection", coBool); + def->label = L("Enable clumping detection"); + def->tooltip = L("Enable clumping detection"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("wrapping_detection_layers", coInt); + def->label = L("Clumping detection layers"); + def->tooltip = L("Clumping detection layers."); + def->min = 0; + def->mode = comDevelop; + def->set_default_value(new ConfigOptionInt(20)); + + def = this->add("wrapping_exclude_area", coPoints); + def->label = L("Probing exclude area of clumping"); + def->tooltip = L("Probing exclude area of clumping."); + def->mode = comAdvanced; + def->gui_type = ConfigOptionDef::GUIType::one_string; + def->set_default_value(new ConfigOptionPoints()); + // QDS //w13 y60 def = this->add("thumbnail_size", coString); @@ -3157,6 +3200,14 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + def = this->add("wrapping_detection_gcode", coString); + def->label = L("Clumping detection G-code"); + def->multiline = true; + def->full_width = true; + def->height = 5; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + def = this->add("silent_mode", coBool); def->label = L("Supports silent mode"); def->tooltip = L("Whether the machine supports silent mode in which machine use lower acceleration to print"); @@ -5312,7 +5363,7 @@ void PrintConfigDef::init_extruder_option_keys() void PrintConfigDef::init_filament_option_keys() { m_filament_option_keys = { - "filament_diameter", "min_layer_height", "max_layer_height", + "filament_diameter", "min_layer_height", "max_layer_height","volumetric_speed_coefficients", "retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed", "retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance", "retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour", @@ -6233,6 +6284,7 @@ std::set filament_options_with_variant = { "filament_retraction_length", "filament_z_hop", "filament_z_hop_types", + "filament_retract_restart_extra", "filament_retraction_speed", "filament_deretraction_speed", "filament_retraction_minimum_travel", @@ -6248,7 +6300,9 @@ std::set filament_options_with_variant = { "nozzle_temperature_initial_layer", "nozzle_temperature", "filament_flush_volumetric_speed", - "filament_flush_temp" + "filament_flush_temp", + "volumetric_speed_coefficients", + "filament_adaptive_volumetric_speed" }; // Parameters that are the same as the number of extruders @@ -6281,7 +6335,6 @@ std::set printer_options_with_variant_1 = { "retract_restart_extra_toolchange", "long_retractions_when_cut", "retraction_distances_when_cut", - "nozzle_volume", "nozzle_type", "printer_extruder_id", "printer_extruder_variant", @@ -6483,7 +6536,11 @@ t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int num_objects, int us ConfigOptionEnum* timelapse_opt = this->option>("timelapse_type"); bool is_smooth_timelapse = timelapse_opt != nullptr && timelapse_opt->value == TimelapseType::tlSmooth; - if (!is_smooth_timelapse && (used_filaments == 1 || (ps_opt->value == PrintSequence::ByObject && num_objects > 1))) { + + ConfigOptionBool *enable_wrapping_opt = this->option("enable_wrapping_detection"); + bool enable_wrapping = enable_wrapping_opt != nullptr && enable_wrapping_opt->value; + + if (!is_smooth_timelapse && !enable_wrapping && (used_filaments == 1 || (ps_opt->value == PrintSequence::ByObject && num_objects > 1))) { if (ept_opt->value) { ept_opt->value = false; changed_keys.push_back("enable_prime_tower"); @@ -6887,7 +6944,7 @@ int DynamicPrintConfig::update_values_from_single_to_multi(DynamicPrintConfig& m //used for object/region config //duplicate single to multiple -int DynamicPrintConfig::update_values_from_single_to_multi_2(DynamicPrintConfig& multi_config, std::set& key_set) +/*int DynamicPrintConfig::update_values_from_single_to_multi_2(DynamicPrintConfig& multi_config, std::set& key_set) { const ConfigDef *config_def = this->def(); if (!config_def) { @@ -6942,32 +6999,52 @@ int DynamicPrintConfig::update_values_from_single_to_multi_2(DynamicPrintConfig& } return 0; -} +}*/ -int DynamicPrintConfig::update_values_from_multi_to_single(DynamicPrintConfig& single_config, std::set& key_set, std::string id_name, std::string variant_name, std::vector& extruder_variants) +//update global process config for multi variant to multi variant case +//1. skip the key-values not in key_set +//2. update the key-value to the new one, then check whether the old one with the same variant can be used or not +int DynamicPrintConfig::update_values_from_multi_to_multi(DynamicPrintConfig& new_config, std::set& key_set, std::string id_name, std::string variant_name, std::vector& new_extruder_variants) { - int extruder_count = extruder_variants.size(); - std::vector extruder_index(extruder_count, -1); + int new_extruder_count = new_extruder_variants.size(); + std::vector new_variant_indices(new_extruder_count, -1); auto print_variant_opt = dynamic_cast(this->option(variant_name)); - if (!print_variant_opt) { - BOOST_LOG_TRIVIAL(error) << boost::format("%1%:%2%, can not get %3% from config")%__FUNCTION__ %__LINE__ % variant_name; + auto new_variant_opt = dynamic_cast(new_config.option(variant_name)); + auto new_print_id_opt = dynamic_cast(new_config.option(id_name)); + if (!print_variant_opt || !new_variant_opt || !new_print_id_opt) { + BOOST_LOG_TRIVIAL(error) << boost::format("%1%:%2%, can not get variant %3%, id %4% from config")%__FUNCTION__ %__LINE__ % variant_name % id_name; return -1; } - int variant_count = print_variant_opt->size(); + int variant_count = print_variant_opt->size(), new_variant_count = new_variant_opt->size(); - auto print_id_opt = dynamic_cast(this->option(id_name)); - if (!print_id_opt) { - BOOST_LOG_TRIVIAL(error) << boost::format("%1%:%2%, can not get %3% from config")%__FUNCTION__ %__LINE__ % id_name; - return -1; - } - - for (int i = 0; i < extruder_count; i++) + std::vector> extruder_variant_indices; + for (int i = 0; i < new_extruder_count; i++) { + std::vector variant_indices; for (int j = 0; j < variant_count; j++) { - if ((i+1 == print_id_opt->values[j]) && (extruder_variants[i] == print_variant_opt->values[j])) { - extruder_index[i] = j; + if (new_extruder_variants[i] == print_variant_opt->values[j]) { + variant_indices.push_back(j); + } + } + + if (variant_indices.empty()) + { + //can not find any + variant_indices.resize(variant_count, 0); + for (int j = 0; j < variant_count; j++) + variant_indices[j] = j; + } + extruder_variant_indices.emplace_back(variant_indices); + } + + for (int i = 0; i < new_extruder_count; i++) + { + for (int j = 0; j < new_variant_count; j++) + { + if ((i+1 == new_print_id_opt->values[j]) && (new_extruder_variants[i] == new_variant_opt->values[j])) { + new_variant_indices[i] = j; break; } } @@ -6988,7 +7065,7 @@ int DynamicPrintConfig::update_values_from_multi_to_single(DynamicPrintConfig& s switch (optdef->type) { case coStrings: { - ConfigOptionStrings* src_opt = single_config.option(key); + ConfigOptionStrings* src_opt = new_config.option(key); if (src_opt) { ConfigOptionStrings* opt = this->option(key, true); @@ -6999,7 +7076,7 @@ int DynamicPrintConfig::update_values_from_multi_to_single(DynamicPrintConfig& s } case coInts: { - ConfigOptionInts* src_opt = single_config.option(key); + ConfigOptionInts* src_opt = new_config.option(key); if (src_opt) { ConfigOptionInts* opt = this->option(key, true); @@ -7010,54 +7087,91 @@ int DynamicPrintConfig::update_values_from_multi_to_single(DynamicPrintConfig& s } case coFloats: { - ConfigOptionFloats* src_opt = single_config.option(key); + ConfigOptionFloats* src_opt = new_config.option(key); if (src_opt) { ConfigOptionFloats* opt = this->option(key, true); std::vector old_values = opt->values; int old_count = old_values.size(); + int new_count = src_opt->values.size(); - //assert(variant_count == opt->size()); + assert(variant_count == old_count); + assert(new_variant_count == new_count); opt->values = src_opt->values; - for (int i = 0; i < extruder_count; i++) + for (int i = 0; i < new_extruder_count; i++) { - assert(extruder_index[i] != -1); - if ((old_count > extruder_index[i]) && (old_values[extruder_index[i]] < opt->values[0])) - opt->values[0] = old_values[extruder_index[i]]; + std::vector& variant_indices = extruder_variant_indices[i]; + int new_variant_index = new_variant_indices[i]; + if ((new_variant_index == -1) || variant_indices.empty()) + continue; + + for(auto idx : variant_indices){ + assert(idx < old_count); + if (old_values[idx] < opt->values[new_variant_index]) + opt->values[new_variant_index] = old_values[idx]; + } } } break; } case coFloatsOrPercents: { - ConfigOptionFloatsOrPercents* src_opt = single_config.option(key); + ConfigOptionFloatsOrPercents* src_opt = new_config.option(key); if (src_opt) { ConfigOptionFloatsOrPercents* opt = this->option(key, true); std::vector old_values = opt->values; int old_count = old_values.size(); + int new_count = src_opt->values.size(); - //assert(variant_count == opt->size()); + assert(variant_count == old_count); + assert(new_variant_count == new_count); opt->values = src_opt->values; - for (int i = 0; i < extruder_count; i++) + for (int i = 0; i < new_extruder_count; i++) { - assert(extruder_index[i] != -1); - if ((old_count > extruder_index[i]) && (old_values[extruder_index[i]] < opt->values[0])) - opt->values[0] = old_values[extruder_index[i]]; + std::vector& variant_indices = extruder_variant_indices[i]; + int new_variant_index = new_variant_indices[i]; + if ((new_variant_index == -1) || variant_indices.empty()) + continue; + + for(auto idx : variant_indices){ + assert(idx < old_count); + if (old_values[idx] < opt->values[new_variant_index]) + opt->values[new_variant_index] = old_values[idx]; + } } } break; } case coBools: { - ConfigOptionBools* src_opt = single_config.option(key); + ConfigOptionBools* src_opt = new_config.option(key); if (src_opt) { ConfigOptionBools* opt = this->option(key, true); - //assert(variant_count == opt->size()); + std::vector old_values = opt->values; + int old_count = old_values.size(); + int new_count = src_opt->values.size(); + + assert(variant_count == old_count); + assert(new_variant_count == new_count); opt->values = src_opt->values; + + for (int i = 0; i < new_extruder_count; i++) + { + std::vector& variant_indices = extruder_variant_indices[i]; + int new_variant_index = new_variant_indices[i]; + if ((new_variant_index == -1) || variant_indices.empty()) + continue; + + for(auto idx : variant_indices){ + assert(idx < old_count); + if (old_values[idx]) //enabled + opt->values[new_variant_index] = old_values[idx]; + } + } } break; @@ -7071,9 +7185,129 @@ int DynamicPrintConfig::update_values_from_multi_to_single(DynamicPrintConfig& s return 0; } +int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector& src_extruder_variants, const std::vector& dst_extruder_variants, const DynamicPrintConfig& dst_config, const std::set& key_sets) +{ + const ConfigDef *config_def = this->def(); + if (!config_def) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: can not find config define")%__LINE__; + return -1; + } + + auto get_same_variant_indices = [](const std::vector& extruder_variants, const std::string& variant){ + std::vector indices; + for(int i=0;i> same_variant_indices; + for(size_t dst_idx =0 ;dst_idx < dst_extruder_variants.size(); ++dst_idx){ + auto& dst_variant = dst_extruder_variants[dst_idx]; + auto indices =get_same_variant_indices(src_extruder_variants, dst_variant); + same_variant_indices.emplace_back(indices); + } + + t_config_option_keys keys = this->keys(); + for(auto& key : keys){ + if(key_sets.find(key) == key_sets.end()) + continue; + const ConfigOptionDef* optdef = config_def->get(key); + if(!optdef){ + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: can not find opt define for %2%")%__LINE__%key; + continue; + } + + switch (optdef->type){ + case coFloats: + { + ConfigOptionFloatsNullable* opt = this->option(key); + auto src_values = opt->values; + auto dst_values = dst_config.option(key) ->values; + for(size_t dst_idx =0; dst_idx < same_variant_indices.size(); ++dst_idx){ + auto& indices = same_variant_indices[dst_idx]; + if(indices.empty()) + continue; + bool has_value = false; + double target_value = std::numeric_limits::max(); + for(auto idx : indices){ + if(opt && !opt->is_nil(idx)){ + has_value = true; + target_value = std::min(target_value, src_values[idx]); + } + } + + if(has_value) + dst_values[dst_idx] = target_value; + } + opt->values = dst_values; + break; + } + case coFloatsOrPercents: + { + ConfigOptionFloatsOrPercentsNullable* opt = this->option(key); + auto src_values = opt->values; + auto dst_values = dst_config.option(key) ->values; + for(size_t dst_idx =0; dst_idx < same_variant_indices.size(); ++dst_idx){ + auto& indices = same_variant_indices[dst_idx]; + if(indices.empty()) + continue; + bool has_value = false; + FloatOrPercent target_value(9999.f, true); + for(auto idx : indices){ + if(opt && !opt->is_nil(idx)){ + has_value = true; + target_value = src_values[idx].value < target_value.value ? src_values[idx] : target_value; + } + } + + if(has_value) + dst_values[dst_idx] = target_value; + } + opt->values = dst_values; + break; + } + case coBools: + { + ConfigOptionBoolsNullable* opt = this->option(key); + auto src_values = opt->values; + auto dst_values = dst_config.option(key) ->values; + for(size_t dst_idx =0; dst_idx < same_variant_indices.size(); ++dst_idx){ + auto indices = same_variant_indices[dst_idx]; + if(indices.empty()) + continue; + bool has_value = false; + bool target_value; + for(auto idx : indices){ + if(opt && !opt->is_nil(idx)){ + has_value = true; + target_value = src_values[idx]; + break; + } + } + + if(has_value) + dst_values[dst_idx] = target_value; + } + + opt->values = dst_values; + break; + } + default: + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: unsupported option type for %2%")%__LINE__%key; + break; + } + + } + + return 0; + +} + + //used for object/region config //use the smallest of multiple to single -int DynamicPrintConfig::update_values_from_multi_to_single_2(std::set& key_set) +/*int DynamicPrintConfig::update_values_from_multi_to_single_2(std::set& key_set) { const ConfigDef *config_def = this->def(); if (!config_def) { @@ -7157,7 +7391,7 @@ int DynamicPrintConfig::update_values_from_multi_to_single_2(std::set DynamicPrintConfig::update_values_to_printer_extruders(DynamicP if (variant_index[e_index] < 0) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: could not found extruder_type %2%, nozzle_volume_type %3%, extruder_index %4%") %__LINE__ %s_keys_names_ExtruderType[extruder_type] % s_keys_names_NozzleVolumeType[nozzle_volume_type] % (e_index+1); - assert(false); + //assert(false); //for some updates happens in a invalid state(caused by popup window) //we need to avoid crash variant_index[e_index] = 0; @@ -7372,9 +7606,9 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen //int extruder_count = opt_nozzle_diameters->size(); auto opt_extruder_type = dynamic_cast(printer_config.option("extruder_type")); auto opt_nozzle_volume_type = dynamic_cast(printer_config.option("nozzle_volume_type")); + auto opt_ids = id_name.empty()? nullptr: dynamic_cast(this->option(id_name)); std::vector variant_index; - variant_index.resize(filament_count, -1); for (int f_index = 0; f_index < filament_count; f_index++) @@ -7391,6 +7625,13 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen //for some updates happens in a invalid state(caused by popup window) //we need to avoid crash variant_index[f_index] = 0; + if (opt_ids) { + for (int i = 0; i < opt_ids->values.size(); i++) + if (opt_ids->values[i] == (f_index+1)) { + variant_index[f_index] = i; + break; + } + } } } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5575427..fab327f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -17,6 +17,7 @@ #define slic3r_PrintConfig_hpp_ #include "libslic3r.h" +#include "CommonDefs.hpp" #include "Config.hpp" #include "Polygon.hpp" #include @@ -260,16 +261,6 @@ enum LayerSeq { flsCutomize }; -// QDS -enum NozzleType { - ntUndefine = 0, - ntHardenedSteel, - ntStainlessSteel, - ntTungstenCarbide, - ntBrass, - ntCount -}; - static std::unordered_mapNozzleTypeEumnToStr = { {NozzleType::ntUndefine, "undefine"}, {NozzleType::ntHardenedSteel, "hardened_steel"}, @@ -551,10 +542,12 @@ public: void update_diff_values_to_child_config(DynamicPrintConfig& new_config, std::string extruder_id_name, std::string extruder_variant_name, std::set& key_set1, std::set& key_set2); int update_values_from_single_to_multi(DynamicPrintConfig& multi_config, std::set& key_set, std::string id_name, std::string variant_name); - int update_values_from_multi_to_single(DynamicPrintConfig& single_config, std::set& key_set, std::string id_name, std::string variant_name, std::vector& extruder_variants); + int update_values_from_multi_to_multi(DynamicPrintConfig& new_config, std::set& key_set, std::string id_name, std::string variant_name, std::vector& extruder_variants); - int update_values_from_single_to_multi_2(DynamicPrintConfig& multi_config, std::set& key_set); - int update_values_from_multi_to_single_2(std::set& key_set); + //int update_values_from_single_to_multi_2(DynamicPrintConfig& multi_config, std::set& key_set); + //int update_values_from_multi_to_single_2(std::set& key_set); + + int update_values_from_multi_to_multi_2(const std::vector& src_extruder_variants, const std::vector& dst_extruder_variants, const DynamicPrintConfig& dst_config, const std::set& key_sets); public: // query filament @@ -1060,6 +1053,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBools, enable_pressure_advance)) ((ConfigOptionFloats, pressure_advance)) ((ConfigOptionFloats, filament_diameter)) + ((ConfigOptionBoolsNullable, filament_adaptive_volumetric_speed)) + ((ConfigOptionStrings, volumetric_speed_coefficients)) ((ConfigOptionInts, filament_adhesiveness_category)) ((ConfigOptionFloats, filament_density)) ((ConfigOptionStrings, filament_type)) @@ -1105,6 +1100,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionIntsNullable, filament_flush_temp)) // QDS ((ConfigOptionBool, scan_first_layer)) + ((ConfigOptionBool, enable_wrapping_detection)) + ((ConfigOptionInt, wrapping_detection_layers)) + ((ConfigOptionPoints, wrapping_exclude_area)) //w12 ((ConfigOptionString, thumbnail_size)) // ((ConfigOptionBool, spaghetti_detector)) @@ -1113,6 +1111,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionEnum, gcode_flavor)) ((ConfigOptionString, layer_change_gcode)) ((ConfigOptionString, time_lapse_gcode)) + ((ConfigOptionString, wrapping_detection_gcode)) //#ifdef HAS_PRESSURE_EQUALIZER // ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive)) // ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative)) @@ -1325,6 +1324,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( //w14 ((ConfigOptionBools, dont_slow_down_outer_wall)) ((ConfigOptionFloats, grab_length)) + ((ConfigOptionFloats, filament_velocity_adaptation_factor)) //QDsS ((ConfigOptionFloats, circle_compensation_speed)) ((ConfigOptionFloats, diameter_limit)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 89bc8b5..e172faa 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -676,6 +676,8 @@ void PrintObject::clear_overhangs_for_lift() static const float g_min_overhang_percent_for_lift = 0.3f; +#define REGISTER_SUPPORTS_FOR_LIFT + void PrintObject::detect_overhangs_for_lift() { if (this->set_started(posDetectOverhangsForLift)) { @@ -688,18 +690,32 @@ void PrintObject::detect_overhangs_for_lift() this->clear_overhangs_for_lift(); tbb::spin_mutex layer_storage_mutex; - tbb::parallel_for(tbb::blocked_range(num_raft_layers + 1, num_layers), - [this, min_overlap](const tbb::blocked_range& range) - { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { - Layer& layer = *m_layers[layer_id]; - Layer& lower_layer = *layer.lower_layer; + tbb::parallel_for(tbb::blocked_range(num_raft_layers + 1, num_layers), [this, min_overlap](const tbb::blocked_range &range) { + for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { + Layer &layer = *m_layers[layer_id]; + Layer &lower_layer = *layer.lower_layer; - ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap))); - layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width))); - layer.loverhangs_bbox = get_extents(layer.loverhangs); + ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap))); + layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width))); + +#ifdef REGISTER_SUPPORTS_FOR_LIFT + // register all supports to avoidance region for lift + if (layer_id < m_support_layers.size()) { + auto &support_layer = m_support_layers[layer_id]; + if (support_layer) { + if (!support_layer->support_islands.empty()) { + append(layer.loverhangs, + std::move(offset2_ex(support_layer->support_islands, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width)))); + } else { + ExPolygons support_infill_polygons = union_ex(support_layer->support_fills.polygons_covered_by_spacing(double(coord_t(SCALED_EPSILON)))); + append(layer.loverhangs, std::move(offset2_ex(support_infill_polygons, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width)))); + } + } } - }); +#endif + layer.loverhangs_bbox = get_extents(layer.loverhangs); + } + }); this->set_done(posDetectOverhangsForLift); } } @@ -709,26 +725,25 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); - if(!has_support() && !m_print->get_no_check_flag()) { + if (!has_support() && !m_print->get_no_check_flag()) { // QDS: pop a warning if objects have significant amount of overhangs but support material is not enabled // Note: we also need to pop warning if support is disabled and only raft is enabled m_print->set_status(50, L("Checking support necessity")); - typedef std::chrono::high_resolution_clock clock_; - typedef std::chrono::duration > second_; - std::chrono::time_point t0{ clock_::now() }; + typedef std::chrono::high_resolution_clock clock_; + typedef std::chrono::duration> second_; + std::chrono::time_point t0{clock_::now()}; SupportNecessaryType sntype = this->is_support_necessary(); - double duration{ std::chrono::duration_cast(clock_::now() - t0).count() }; + double duration{std::chrono::duration_cast(clock_::now() - t0).count()}; BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(0) << "is_support_necessary takes " << duration << " secs."; if (sntype != NoNeedSupp) { - std::map reasons = { - {SharpTail,L("floating regions")}, - {Cantilever,L("floating cantilever")}, - {LargeOverhang,L("large overhangs")} }; - std::string warning_message = Slic3r::format(L("It seems object %s has %s. Please re-orient the object or enable support generation."), - this->model_object()->name, reasons[sntype]); + std::map reasons = {{SharpTail, L("floating regions")}, + {Cantilever, L("floating cantilever")}, + {LargeOverhang, L("large overhangs")}}; + std::string warning_message = Slic3r::format(L("It seems object %s has %s. Please re-orient the object or enable support generation."), + this->model_object()->name, reasons[sntype]); this->active_step_add_warning(PrintStateBase::WarningLevel::NON_CRITICAL, warning_message, PrintStateBase::SlicingNeedSupportOn); } diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 6bbe4f7..3b84215 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -176,9 +176,9 @@ std::pair generate_interfa const SupportGeneratorLayer &top_contact_layer = *top_contacts[idx_top_contact]; const bool is_top_contact = is_approx(top_contact_layer.bottom_z, intermediate_layers[num_intermediate - 1]->print_z); if (is_top_contact) { - if (idx_intermediate_layer > num_intermediate - support_params.num_top_interface_layers) + if (idx_intermediate_layer > num_intermediate - int(support_params.num_top_interface_layers)) polygons_append(polygons_top_contact_projected_interface, snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); - else if (idx_intermediate_layer > num_intermediate - support_params.num_top_interface_layers - support_params.num_top_base_interface_layers) + else if (idx_intermediate_layer > num_intermediate - int(support_params.num_top_interface_layers) - int(support_params.num_top_base_interface_layers)) polygons_append(polygons_top_contact_projected_base, snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); } else { if (top_contact_layer.print_z - EPSILON < diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index ef375ab..47d472d 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -3898,18 +3898,17 @@ void TreeSupport::generate_contact_points() bool add_interface = (force_tip_to_roof || area(overhang_part) > minimum_roof_area); const auto &relevant_forbidden = get_collision(0, layer_nr - 1); ExPolygons overhangs{overhang_part}; - ExPolygons overhangs_regular, overhangs_no_extra_expand; - if (add_interface && xy_expansion > EPSILON && !is_sharp_tail) { - overhangs = safe_offset_inc(overhangs, xy_expansion, relevant_forbidden, scale_(MIN_BRANCH_RADIUS * 1.75), 0, 1); - } - overhangs_no_extra_expand = (!is_sharp_tail && (unscale_(xy_expansion) - config.support_expansion.value > EPSILON) && (config.support_expansion.value > EPSILON)) ? - safe_offset_inc({overhang_part}, scale_(config.support_expansion.value), relevant_forbidden, scale_(MIN_BRANCH_RADIUS * 1.75), 0, 1) : - overhangs; + ExPolygons overhangs_regular; + if (!is_sharp_tail && (config.support_expansion.value > EPSILON)) + overhangs = safe_offset_inc({overhang_part}, scale_(config.support_expansion.value), relevant_forbidden, scale_(MIN_BRANCH_RADIUS * 1.75), 0, 1); if (m_support_params.support_style == smsTreeHybrid && (overhang_type & (BigFlat | ThinPlate))) { - overhangs_regular = offset_ex(intersection_ex(overhangs, m_ts_data->m_layer_outlines_below[layer_nr - 1]), radius_scaled); - ExPolygons overhangs_normal = offset2_ex(diff_ex(overhangs, overhangs_regular),scale_(extrusion_width),-scale_(extrusion_width)); - overhangs_regular = intersection_ex(overhangs_regular, overhangs_no_extra_expand); + overhangs_regular = intersection_ex(overhangs, offset_ex(m_ts_data->m_layer_outlines_below[layer_nr - 1], scale_(config.support_object_xy_distance.value))); + ExPolygons overhangs_normal = offset2_ex(diff_ex({overhang_part}, overhangs_regular), scale_(extrusion_width), -scale_(extrusion_width)); + if (add_interface && xy_expansion > EPSILON && !is_sharp_tail) + overhangs_normal = safe_offset_inc(overhangs_normal, xy_expansion, + offset_ex(m_ts_data->m_layer_outlines_below[layer_nr - 1], scale_(config.support_object_xy_distance.value)), + scale_(MIN_BRANCH_RADIUS * 1.75), 0, 1); // if the outside area is still big, we can need normal nodes coord_t gap_width = scale_(extrusion_width / 2.) + scale_(m_ts_data->m_xy_distance); ExPolygons overhangs_normal_split; @@ -3922,7 +3921,7 @@ void TreeSupport::generate_contact_points() } for (auto &overhang : overhangs_normal_split) { if (!is_stable(layer->bottom_z(), overhang, 0) || overhang.area() < SQ(scale_(2.))) { - ExPolygons unstable_overhangs = intersection_ex({overhang}, overhangs_no_extra_expand); + ExPolygons unstable_overhangs = intersection_ex({overhang}, overhangs); overhangs_regular.insert(overhangs_regular.end(), unstable_overhangs.begin(), unstable_overhangs.end()); continue; } @@ -3936,7 +3935,7 @@ void TreeSupport::generate_contact_points() } } else{ - overhangs_regular = overhangs_no_extra_expand; + overhangs_regular = overhangs; } for (auto &overhang : overhangs_regular) { diff --git a/src/libslic3r/TriangleSelector.cpp b/src/libslic3r/TriangleSelector.cpp index c0c4006..c2d282d 100644 --- a/src/libslic3r/TriangleSelector.cpp +++ b/src/libslic3r/TriangleSelector.cpp @@ -1674,7 +1674,7 @@ void TriangleSelector::get_seed_fill_its_recursive( idx_set.insert(facet_idx); append_touching_its(facet_idx, its); for (int i = 0; i < 3; i++) { - if (select_state == m_triangles[neighbors(i)].get_state()) { + if (neighbors(i) >= 0 && neighbors(i) < m_triangles.size() && select_state == m_triangles[neighbors(i)].get_state()) { if (idx_set.find(neighbors(i)) == idx_set.end()) { idx_set.insert(neighbors(i)); append_touching_its(neighbors(i), its); diff --git a/src/libslic3r/TriangleSelector.hpp b/src/libslic3r/TriangleSelector.hpp index 696dc4c..4d39637 100644 --- a/src/libslic3r/TriangleSelector.hpp +++ b/src/libslic3r/TriangleSelector.hpp @@ -317,7 +317,7 @@ protected: // Get/set current state. void set_state(EnforcerBlockerType type) { assert(!is_split()); state = type; } - EnforcerBlockerType get_state() const { assert(! is_split()); return state; } + EnforcerBlockerType get_state() const { return state; } // Set if the triangle has been selected or unselected by seed fill. void select_by_seed_fill(); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 4423700..97eff21 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -70,6 +70,8 @@ #define CLI_GCODE_PATH_CONFLICTS -101 #define CLI_GCODE_PATH_IN_UNPRINTABLE_AREA -102 #define CLI_FILAMENT_UNPRINTABLE_ON_FIRST_LAYER -103 +#define CLI_GCODE_PATH_OUTSIDE -104 +#define CLI_GCODE_IN_WRAPPING_DETECT_AREA -105 namespace boost { namespace filesystem { class directory_entry; }} @@ -149,6 +151,7 @@ private: inline static size_t start_pos = std::string::npos; inline static size_t id_start_pos = std::string::npos; inline static size_t name_size = 0; + inline static std::string full; static bool init_usrname_range() { @@ -183,7 +186,7 @@ private: if (!env) { return false; } - std::string full(env); + full = std::string(env); size_t sep_pos = full.find_last_of("\\/"); if (sep_pos == std::string::npos) { return false; @@ -201,25 +204,37 @@ private: return true; } + static inline std::string file_name(const std::string &name) { + return boost::filesystem::path(name).filename().string(); + } + static std::string sanitize_impl(const std::string &raw) { if (!init_usrname_range()) { - return raw; + return file_name(raw); } - if (raw.length() < start_pos + name_size) { - return raw; + if (raw.length() < full.length() || raw.empty()) { + return file_name(raw); } std::string sanitized = raw; + if (raw[0] != full[0] || raw[full.length() - 1] != full[full.length() - 1]) { + if (std::isupper(raw[start_pos]) && std::tolower(raw[start_pos]) == full[start_pos]) { + sanitized.replace(start_pos, 12, std::string(12, '*')); + return sanitized; + } + return file_name(raw); + } + if (raw[start_pos + name_size] == '\\' || raw[start_pos + name_size] == '/') { sanitized.replace(start_pos, name_size, std::string(name_size, '*')); - } else if (std::isupper(raw[start_pos])) { + } else if (std::isupper(raw[start_pos]) && std::tolower(raw[start_pos]) == full[start_pos]) { sanitized.replace(start_pos, 12, std::string(12, '*')); } else { - return raw; + return file_name(raw); } - + if (id_start_pos != std::string::npos && id_start_pos < sanitized.length() && (sanitized[id_start_pos - 1] == '\\' || sanitized[id_start_pos - 1] == '/') && std::isdigit(sanitized[id_start_pos])) { // If the ID part is present, sanitize it as well @@ -230,25 +245,33 @@ private: sanitized.replace(id_start_pos, id_end_pos - id_start_pos, std::string(id_end_pos - id_start_pos, '*')); } - return sanitized; + return file_name(sanitized); } static std::string sanitize_impl(std::string &&raw) { if (!init_usrname_range()) { - return raw; + return file_name(raw); } - if (raw.length() < start_pos + name_size) { - return raw; + if (raw.length() < full.length() || raw.empty()) { + return std::move(file_name(raw)); + } + + if (raw[0] != full[0] || raw[full.length() - 1] != full[full.length() - 1]) { + if (std::isupper(raw[start_pos]) && std::tolower(raw[start_pos]) == full[start_pos]) { + raw.replace(start_pos, 12, std::string(12, '*')); + return std::move(file_name(raw)); + } + return std::move(file_name(raw)); } if (raw[start_pos + name_size] == '\\' || raw[start_pos + name_size] == '/') { raw.replace(start_pos, name_size, std::string(name_size, '*')); - } else if (std::isupper(raw[start_pos])) { + } else if (std::isupper(raw[start_pos]) && std::tolower(raw[start_pos]) == full[start_pos]) { raw.replace(start_pos, 12, std::string(12, '*')); } else { - return raw; + return std::move(file_name(raw)); } if (id_start_pos != std::string::npos && id_start_pos < raw.length() && (raw[id_start_pos - 1] == '\\' || raw[id_start_pos - 1] == '/') && @@ -261,7 +284,7 @@ private: raw.replace(id_start_pos, id_end_pos - id_start_pos, std::string(id_end_pos - id_start_pos, '*')); } - return std::move(raw); + return std::move(file_name(raw)); } }; diff --git a/src/platform/unix/build_appimage.sh.in b/src/platform/unix/build_appimage.sh.in index 8e67e86..6732037 100644 --- a/src/platform/unix/build_appimage.sh.in +++ b/src/platform/unix/build_appimage.sh.in @@ -1,5 +1,5 @@ #!/bin/sh -APPIMAGETOOLURL="https://github.com/AppImage/AppImageKit/releases/latest/download/appimagetool-x86_64.AppImage" +APPIMAGETOOLURL="https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" APP_IMAGE="@SLIC3R_APP_KEY@_ubu64.AppImage"