From bf66c447b12fde018ac1d85b562b6b871a50388c Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 18 Jan 2024 18:54:36 +0100 Subject: [PATCH 1/6] Scripted widget: now can have dependencies in other tabs & update all values of a vector instead of the first one. --- src/slic3r/GUI/ScriptExecutor.cpp | 34 ++++++++----- src/slic3r/GUI/Tab.cpp | 80 ++++++++++++++++++------------- src/slic3r/GUI/Tab.hpp | 4 +- 3 files changed, 72 insertions(+), 46 deletions(-) diff --git a/src/slic3r/GUI/ScriptExecutor.cpp b/src/slic3r/GUI/ScriptExecutor.cpp index 923d1a50dad..99bc3b438b1 100644 --- a/src/slic3r/GUI/ScriptExecutor.cpp +++ b/src/slic3r/GUI/ScriptExecutor.cpp @@ -82,7 +82,7 @@ std::pair get_coll(const std::stri bool as_get_bool(std::string& key) { const ConfigOption* opt = get_coll(key).second; - if (opt == nullptr || opt->type() != ConfigOptionType::coBool) + if (opt == nullptr || (opt->type() != ConfigOptionType::coBool && opt->type() != ConfigOptionType::coBools)) throw NoDefinitionException("error, can't find bool option " + key); if (opt->is_vector()) { const ConfigOptionVectorBase* vector = static_cast(opt); @@ -102,18 +102,20 @@ void as_set_bool(std::string& key, bool b) conf.set_key_value(key, new ConfigOptionBool(b)); } else if (result.second->type() == ConfigOptionType::coBools) { ConfigOptionBools* new_val = static_cast(result.second->clone()); - new_val->set_at(b, 0); + for(size_t i=0; isize(); ++i) + new_val->set_at(b, i); conf.set_key_value(key, new_val); } } int32_t as_get_int(std::string& key) { + const ConfigOption* opt = get_coll(key).second; - if (opt == nullptr || (opt->type() != ConfigOptionType::coInt && opt->type() != ConfigOptionType::coEnum)) + if (opt == nullptr || (opt->type() != ConfigOptionType::coInt && opt->type() != ConfigOptionType::coInts && opt->type() != ConfigOptionType::coEnum)) throw NoDefinitionException("error, can't find int option " + key); if (opt->is_vector()) { const ConfigOptionVectorBase* vector = static_cast(opt); - return (int32_t)vector->get_float(0); + return (int32_t)vector->get_int(0); } else { return (int32_t)(opt->get_int()); } @@ -129,7 +131,8 @@ void as_set_int(std::string& key, int val) conf.set_key_value(key, new ConfigOptionInt(val)); } else if (result.second->type() == ConfigOptionType::coInts) { ConfigOptionInts* new_val = static_cast(result.second->clone()); - new_val->set_at(val, 0); + for(size_t i=0; isize(); ++i) + new_val->set_at(val, i); conf.set_key_value(key, new_val); } else if (result.second->type() == ConfigOptionType::coEnum) { //const ConfigOptionDef* def = result.first->get_edited_preset().config.get_option_def(key); @@ -202,7 +205,8 @@ void as_set_float(std::string& key, float f_val) if (std::abs(old_value - new_val) / std::abs(old_value) < 0.0000001) new_val = old_value; } - new_opt->set_at(new_val, 0); + for(size_t i=0; isize(); ++i) + new_opt->set_at(new_val, i); conf.set_key_value(key, new_opt); } else if (result.second->type() == ConfigOptionType::coPercent) { double percent_f = floor(f_val * 100000. + 0.5) / 1000.; @@ -220,7 +224,8 @@ void as_set_float(std::string& key, float f_val) if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } - new_opt->set_at(percent_f, 0); + for(size_t i=0; isize(); ++i) + new_opt->set_at(percent_f, i); conf.set_key_value(key, new_opt); } else if (result.second->type() == ConfigOptionType::coFloatOrPercent) { double new_val = round(f_val); @@ -240,7 +245,8 @@ void as_set_float(std::string& key, float f_val) if (std::abs(old_value - new_val) / std::abs(old_value) < 0.0000001) new_val = old_value; } - new_opt->set_at(FloatOrPercent{ new_val, false}, 0); + for(size_t i=0; isize(); ++i) + new_opt->set_at(FloatOrPercent{ new_val, false}, i); conf.set_key_value(key, new_opt); } } @@ -275,7 +281,8 @@ void as_set_percent(std::string& key, float f_val) if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } - new_opt->set_at(percent_f / 100., 0); + for(size_t i=0; isize(); ++i) + new_opt->set_at(percent_f / 100., i); conf.set_key_value(key, new_opt); } else if (result.second->type() == ConfigOptionType::coPercent) { // only update if difference is significant @@ -291,7 +298,8 @@ void as_set_percent(std::string& key, float f_val) if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } - new_opt->set_at(percent_f, 0); + for(size_t i=0; isize(); ++i) + new_opt->set_at(percent_f, i); conf.set_key_value(key, new_opt); } else if (result.second->type() == ConfigOptionType::coFloatOrPercent) { if (static_cast(result.second)->percent) { @@ -309,7 +317,8 @@ void as_set_percent(std::string& key, float f_val) if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } - new_opt->set_at(FloatOrPercent{ percent_f, true }, 0); + for(size_t i=0; isize(); ++i) + new_opt->set_at(FloatOrPercent{ percent_f, true }, i); conf.set_key_value(key, new_opt); } } @@ -338,7 +347,8 @@ void as_set_string(std::string& key, std::string& val) conf.set_key_value(key, new ConfigOptionString(val)); } else if (result.second->type() == ConfigOptionType::coStrings) { ConfigOptionStrings* new_val = (ConfigOptionStrings*)result.second->clone(); - new_val->set_at(val, 0); + for(size_t i=0; isize(); ++i) + new_val->set_at(val, i); conf.set_key_value(key, new_val); } else if (result.second->type() == ConfigOptionType::coEnum) { const ConfigOptionDef* def = result.first->get_edited_preset().config.get_option_def(key); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a234e67b69a..2841f49ef46 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -495,7 +495,7 @@ Slic3r::GUI::PageShp Tab::create_options_page(const wxString& title, const std:: wxString Tab::translate_category(const wxString& title, Preset::Type preset_type) { if (preset_type == Preset::TYPE_PRINTER && title.Contains("Extruder ")) { - return _("Extruder") + title.SubString(8, title.Last()); + return _("Extruder") + title.SubString(strlen("Extruder"), title.Last()); } return _(title); } @@ -1315,23 +1315,35 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) PrinterTechnology pt = get_printer_technology(); ConfigOptionsGroup* og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(pt); + //create optid without index + //TODO remove this #idx embeeded inside and use a struct! + std::string opt_id = opt_key; + if(size_t pos = opt_id.find("#"); pos != std::string::npos) + opt_id = opt_id.substr(0, pos); + // script presets - if (this->m_script_exec.is_intialized()) { - auto it = deps_id_2_script_ids.find(opt_key); - if (it != deps_id_2_script_ids.end()) { - for (const std::string& preset_id : it->second) { - for (PageShp page : m_pages) { - Field* field = page->get_field(preset_id, -1); + auto it = Tab::depsid_2_tabtype_scriptids.find(opt_id); + if (it != Tab::depsid_2_tabtype_scriptids.end()) { + for (const std::pair &tabtype_presetid : it->second) { + Tab *script_tab; + if (this->type() == tabtype_presetid.first) { + script_tab = this; + } else { + script_tab = wxGetApp().get_tab(tabtype_presetid.first); + } + if (script_tab && script_tab->m_script_exec.is_intialized()) { + for (PageShp &page : script_tab->m_pages) { + Field *field = page->get_field(tabtype_presetid.second, -1); if (field) { - boost::any script_val = this->m_script_exec.call_script_function_get_value(field->m_opt); + boost::any script_val = script_tab->m_script_exec.call_script_function_get_value(field->m_opt); if (!script_val.empty()) field->set_any_value(script_val, false); } } - { // also check freq changed params - Field* field = og_freq_chng_params->get_field(preset_id); + if((script_tab->type() & Preset::Type::TYPE_PRINT1) != 0) { // also check freq changed params (print tab is in chrage of these ones) + Field *field = og_freq_chng_params->get_field(tabtype_presetid.second); if (field) { - boost::any script_val = this->m_script_exec.call_script_function_get_value(field->m_opt); + boost::any script_val = script_tab->m_script_exec.call_script_function_get_value(field->m_opt); if (!script_val.empty()) field->set_any_value(script_val, false); } @@ -1813,15 +1825,15 @@ std::vector Tab::create_pages(std::string setting_type_nam current_group = current_page->new_optgroup(_(params.back()), no_title, !no_search, type_override); for (int i = 1; i < params.size() - 1; i++) { if (boost::starts_with(params[i], "title_width$")) { - current_group->title_width = atoi(params[i].substr(12, params[i].size() - 12).c_str()); + current_group->title_width = atoi(params[i].substr(strlen("title_width$")).c_str()); } else if (params[i].find("label_width$") != std::string::npos) { - current_group->label_width = atoi(params[i].substr(12, params[i].size() - 12).c_str()); + current_group->label_width = atoi(params[i].substr(strlen("label_width$")).c_str()); } else if (params[i].find("sidetext_width$") != std::string::npos) { - current_group->sidetext_width = atoi(params[i].substr(15, params[i].size() - 15).c_str()); + current_group->sidetext_width = atoi(params[i].substr(strlen("sidetext_width$")).c_str()); } else if (params[i] == "extruders_count_event") { TabPrinter* tab = nullptr; if ((tab = dynamic_cast(this)) == nullptr) continue; @@ -1967,7 +1979,7 @@ std::vector Tab::create_pages(std::string setting_type_nam current_line = { _L(params.empty()?"":params.back().c_str()), wxString{""} }; for (int i = 1; i < params.size() - 1; i++) { if (boost::starts_with(params[i], "url$")) { // only on line - current_line.label_path = params[i].substr(4, params[i].size() - 4); + current_line.label_path = params[i].substr(strlen("url$")); } } in_line = true; @@ -2007,7 +2019,7 @@ std::vector Tab::create_pages(std::string setting_type_nam int id = -1; for (int i = 1; i < params.size() - 1; i++) { if (boost::starts_with(params[i], "id$")) - id = atoi(params[i].substr(3, params[i].size() - 3).c_str()); + id = atoi(params[i].substr(strlen("id$")).c_str()); else if (params[i] == "idx") id = idx_page; } @@ -2093,9 +2105,9 @@ std::vector Tab::create_pages(std::string setting_type_nam option.opt.mode |= it->second; } } - else if (params[i] == "full_label") + else if (params[i] == "full_label$") { - option.opt.full_label = (params[i].substr(11, params[i].size() - 11)); + option.opt.full_label = (params[i].substr(strlen("full_label$"))); need_to_notified_search = true; } else if (params[i] == "full_label") @@ -2108,49 +2120,49 @@ std::vector Tab::create_pages(std::string setting_type_nam // store current label into full_label if no full_label to prevent rpoblem in the rest of the gui (all empty). if (option.opt.full_label.empty() && !is_script) option.opt.full_label = option.opt.label; - option.opt.label = (params[i].substr(6, params[i].size() - 6)); + option.opt.label = (params[i].substr(strlen("label$"))); if (is_script && option.opt.full_label.empty()) option.opt.full_label = option.opt.label; need_to_notified_search = true; } else if (boost::starts_with(params[i], "label_width$")) { - option.opt.label_width = atoi(params[i].substr(12, params[i].size() - 12).c_str()); + option.opt.label_width = atoi(params[i].substr(strlen("label_width$")).c_str()); } else if (boost::starts_with(params[i], "label_left")) { option.opt.aligned_label_left = true; } else if (boost::starts_with(params[i], "sidetext$")) { - option.opt.sidetext = (params[i].substr(9, params[i].size() - 9)); + option.opt.sidetext = (params[i].substr(strlen("sidetext$"))); } else if (boost::starts_with(params[i], "sidetext_width$")) { - option.opt.sidetext_width = atoi(params[i].substr(15, params[i].size() - 15).c_str()); + option.opt.sidetext_width = atoi(params[i].substr(strlen("sidetext_width$")).c_str()); } else if (params[i] == "full_width") { option.opt.full_width = true; } else if (boost::starts_with(params[i], "width$")) { - option.opt.width = atoi(params[i].substr(6, params[i].size() - 6).c_str()); + option.opt.width = atoi(params[i].substr(strlen("width$")).c_str()); #ifdef __WXGTK3__ option.opt.width += 4; // add width for the big [-][+] buttons #endif } else if (boost::starts_with(params[i], "height$")) { - option.opt.height = atoi(params[i].substr(7, params[i].size() - 7).c_str()); + option.opt.height = atoi(params[i].substr(strlen("height$")).c_str()); } else if (boost::starts_with(params[i], "precision$")) { - option.opt.precision = atoi(params[i].substr(7, params[i].size() - 7).c_str()); + option.opt.precision = atoi(params[i].substr(strlen("precision$")).c_str()); } else if (params[i] == "color") { colored = true; } else if (boost::starts_with(params[i], "url$")) { // only on line - label_path = params[i].substr(4, params[i].size() - 4); + label_path = params[i].substr(strlen("url$")); } else if (boost::starts_with(params[i], "tooltip$")) { - option.opt.tooltip = (params[i].substr(8, params[i].size() - 8)); + option.opt.tooltip = (params[i].substr(strlen("tooltip$"))); boost::replace_all(option.opt.tooltip, "\\n", "\n"); boost::replace_all(option.opt.tooltip, "\\t", "\t"); boost::replace_all(option.opt.tooltip, "\\.", ":"); @@ -2160,9 +2172,11 @@ std::vector Tab::create_pages(std::string setting_type_nam else if (boost::starts_with(params[i], "max_literal$")) { if(params[i].back() == '%') - option.opt.max_literal = { boost::lexical_cast(params[i].substr(12, params[i].size() - 13).c_str()), true }; + option.opt.max_literal = { boost::lexical_cast( + params[i].substr(strlen("max_literal$"), params[i].size() - (strlen("max_literal$")+1)).c_str()), true }; else - option.opt.max_literal = { boost::lexical_cast(params[i].substr(12, params[i].size() - 12).c_str()), false }; + option.opt.max_literal = { boost::lexical_cast( + params[i].substr(strlen("max_literal$")).c_str()), false }; } else if (is_script) { //be careful, "floatX" has to deteted before "float". @@ -2230,7 +2244,7 @@ std::vector Tab::create_pages(std::string setting_type_nam std::vector depends_str; boost::split(depends_str, params[i], boost::is_any_of("$")); for (size_t idx = 1; idx < depends_str.size(); ++idx) { - this->deps_id_2_script_ids[depends_str[idx]].push_back(option.opt.opt_key); + Tab::depsid_2_tabtype_scriptids[depends_str[idx]].emplace_back(this->type(), option.opt.opt_key); option.opt.depends_on.push_back(depends_str[idx]); } } @@ -2264,8 +2278,8 @@ std::vector Tab::create_pages(std::string setting_type_nam if (logs) Slic3r::slic3r_log->info("settings gui") << "create setting " << setting_id <<" with label "<< option.opt.label << "and height "<< option.opt.height<<" fw:"<< option.opt.full_width << "\n"; } else if (boost::starts_with(full_line, "height")) { std::string arg = ""; - if (full_line.size() > 6 && full_line.find(":") != std::string::npos) - arg = full_line.substr(full_line.find(":") + 1, full_line.size() - 1 - full_line.find(":")); + if (size_t dblp_pos = full_line.find(":"); full_line.size() > 6 && dblp_pos != std::string::npos) + arg = full_line.substr(dblp_pos + 1, full_line.size() - 1 - dblp_pos); while (arg.size() > 1 && (arg.back() == ' ' || arg.back() == '\t')) arg = arg.substr(0, arg.size() - 1); height = atoi(arg.c_str()); } else if (full_line == "freq_purging_volumes") { @@ -2492,7 +2506,7 @@ std::vector Tab::create_pages(std::string setting_type_nam [tab](wxWindow *parent) { return tab->create_bed_shape_widget(parent); }); } else if (boost::starts_with(full_line, "vector_line:")) { // extract setting name - std::string opt_key = full_line.substr(12); + std::string opt_key = full_line.substr(strlen("vector_line:")); // create the line std::shared_ptr manager = std::make_shared(); this->m_vector_managers.push_back(manager); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index f601c01d59d..bbbb21f795b 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -311,7 +311,9 @@ class Tab: public wxPanel bool m_show_incompatible_presets; ScriptContainer m_script_exec; - std::unordered_map> deps_id_2_script_ids; + static inline std::unordered_map>> depsid_2_tabtype_scriptids; + //static void register_setting_dependency(Tab &tab_script, std::string opt_id, Preset::Type dep_type, std::string dependency_opt_id); + //static void emit_dependency(Tab &tab_opt_changed, std::string opt_changed); std::vector m_dependent_tabs; enum OptStatus { From 502bd2984eab7a841723cca1f958ebf5de35894e Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 18 Jan 2024 22:12:49 +0100 Subject: [PATCH 2/6] Change fill_density tooltip to make people aware of solid_infill_every_layers supermerill/SuperSlicer#4064 --- src/libslic3r/PrintConfig.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e34aa7dc806..b72026d503b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2331,7 +2331,10 @@ void PrintConfigDef::init_fff_params() def->gui_flags = "show_value"; def->label = L("Fill density"); def->category = OptionCategory::infill; - def->tooltip = L("Density of internal infill, expressed in the range 0% - 100%."); + def->tooltip = L("Density of internal infill, expressed in the range 0% - 100%." + "\nSet 0 to remove any sparse infill." + "\nNote that using a value of 100% won't change the type of infill from sparse to solid." + " If you want only solid infill, you can set the 'Solid infill every X layers' (solid_infill_every_layers) to 1 instead."); def->sidetext = L("%"); def->min = 0; def->max = 100; @@ -2347,7 +2350,6 @@ void PrintConfigDef::init_fff_params() def->enum_values.push_back("42"); def->enum_values.push_back("55"); def->enum_values.push_back("75"); - def->enum_values.push_back("100"); def->enum_labels.push_back("0"); def->enum_labels.push_back("4"); def->enum_labels.push_back("5.5"); @@ -2360,7 +2362,6 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back("42"); def->enum_labels.push_back("55"); def->enum_labels.push_back("75"); - def->enum_labels.push_back("100"); def->mode = comSimpleAE | comPrusa; def->set_default_value(new ConfigOptionPercent(18)); From 9e88cccda11f478b668cf10b7087d1e9d834d6ba Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Jan 2024 10:02:18 +0100 Subject: [PATCH 3/6] disable external_perimeters_vase if perimeter_loop supermerill/SuperSlicer#4027 also better assert --- src/libslic3r/Fill/Fill.cpp | 5 +++-- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 7b01ddefe73..eb2dfc79b92 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -696,8 +696,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: double real_surface = 0; for(auto &t : temp) real_surface += t.area(); assert(compute_volume.volume < unscaled(unscaled(surface_fill.surface.area())) * surface_fill.params.layer_height + EPSILON); - assert(compute_volume.volume < unscaled(unscaled(real_surface)) * surface_fill.params.layer_height * 1.001); - assert(compute_volume.volume > unscaled(unscaled(real_surface)) * surface_fill.params.layer_height * 0.999); + double area = unscaled(unscaled(real_surface)); + assert(compute_volume.volume < area * surface_fill.params.layer_height * 1.001); + assert(compute_volume.volume > area * surface_fill.params.layer_height * 0.999 || area < std::max(1.,surface_fill.params.config->solid_infill_below_area.value)); } #endif } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index f787582510f..2ff5bfee7d5 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -349,7 +349,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "wall_distribution_count", "min_feature_size", "min_bead_width", "aaa" }) toggle_field(el, have_arachne); - toggle_field("external_perimeters_vase", config->opt_bool("external_perimeters_first")); + toggle_field("external_perimeters_vase", config->opt_bool("external_perimeters_first") && !config->opt_bool("perimeter_loop")); for (auto el : { "external_perimeters_nothole", "external_perimeters_hole", "perimeter_bonding"}) toggle_field(el, config->opt_bool("external_perimeters_first") && !have_arachne); From b2be98a0899b8e3c80bf77243dde4c35f80b1060 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Jan 2024 16:05:27 +0100 Subject: [PATCH 4/6] Better handling of too small extrusions for thin_walls_merge & no seam on the thin walls (unless it's a thin wall perimeter loop). --- src/libslic3r/ExtrusionEntity.cpp | 48 +++++++++++++++ src/libslic3r/ExtrusionEntity.hpp | 34 +++++++++- src/libslic3r/Fill/FillBase.cpp | 2 +- src/libslic3r/GCode/SeamPlacer.cpp | 66 ++++++++++++-------- src/libslic3r/GCode/ToolOrdering.cpp | 21 ++++--- src/libslic3r/PerimeterGenerator.cpp | 92 +++++++++++++++++++++------- src/libslic3r/SupportMaterial.cpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 8 ++- 8 files changed, 211 insertions(+), 61 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 0d83edd8c98..b116ae77ba3 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -134,6 +134,18 @@ double ExtrusionLoop::length() const return len; } +ExtrusionRole ExtrusionLoop::role() const +{ + if (this->paths.empty()) + return erNone; + ExtrusionRole role = this->paths.front().role(); + for (const ExtrusionPath &path : this->paths) + if (role != path.role()) { + return erMixed; + } + return role; +} + bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsilon) { for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) { @@ -544,6 +556,42 @@ void ExtrusionVisitorRecursive::use(ExtrusionEntityCollection& collection) { } } +void HasRoleVisitor::use(const ExtrusionMultiPath& multipath) { + for (const ExtrusionPath& path : multipath.paths) { + path.visit(*this); + if(found) return; + } +} +void HasRoleVisitor::use(const ExtrusionMultiPath3D& multipath3D) { + for (const ExtrusionPath3D& path3D : multipath3D.paths) { + path3D.visit(*this); + if(found) return; + } +} +void HasRoleVisitor::use(const ExtrusionLoop& loop) { + for (const ExtrusionPath& path : loop.paths) { + path.visit(*this); + if(found) return; + } +} +void HasRoleVisitor::use(const ExtrusionEntityCollection& collection) { + for (const ExtrusionEntity* entity : collection.entities()) { + entity->visit(*this); + if(found) return; + } +} +bool HasRoleVisitor::search(const ExtrusionEntity &entity, HasRoleVisitor&& visitor) { + entity.visit(visitor); + return visitor.found; +} +bool HasRoleVisitor::search(const ExtrusionEntitiesPtr &entities, HasRoleVisitor&& visitor) { + for (ExtrusionEntity *ptr : entities) { + ptr->visit(visitor); + if (visitor.found) return true; + } + return visitor.found; +} + //class ExtrusionTreeVisitor : ExtrusionVisitor { //public: // //virtual void use(ExtrusionEntity &entity) { assert(false); }; diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 719f69cce0c..d8ab38dc3ec 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -345,7 +345,17 @@ class ExtrusionMultiEntity : public ExtrusionEntity { ExtrusionMultiEntity& operator=(ExtrusionMultiEntity &&rhs) { this->paths = std::move(rhs.paths); return *this; } bool is_loop() const override { return false; } - ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } + ExtrusionRole role() const override + { + if (this->paths.empty()) + return erNone; + ExtrusionRole role = this->paths.front().role(); + for (const ExtrusionPath &path : this->paths) + if (role != path.role()) { + return erMixed; + } + return role; + } virtual const Point& first_point() const override { return this->paths.front().polyline.as_polyline().front(); } virtual const Point& last_point() const override { return this->paths.back().polyline.as_polyline().back(); } @@ -480,7 +490,7 @@ class ExtrusionLoop : public ExtrusionEntity // Test, whether the point is extruded by a bridging flow. // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. bool has_overhang_point(const Point &point) const; - ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } + ExtrusionRole role() const override; ExtrusionLoopRole loop_role() const { return m_loop_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. @@ -674,6 +684,23 @@ class ExtrusionVisitorRecursive : public ExtrusionVisitor { virtual void use(ExtrusionEntityCollection& collection) override; }; +class HasRoleVisitor : public ExtrusionVisitorConst{ +public: + bool found = false; + void use(const ExtrusionMultiPath& multipath) override; + void use(const ExtrusionMultiPath3D& multipath3D) override; + void use(const ExtrusionLoop& loop) override; + void use(const ExtrusionEntityCollection& collection) override; + static bool search(const ExtrusionEntity &entity, HasRoleVisitor&& visitor); + static bool search(const ExtrusionEntitiesPtr &entities, HasRoleVisitor&& visitor); +}; +struct HasInfillVisitor : public HasRoleVisitor{ + void default_use(const ExtrusionEntity &entity) override { found = is_infill(entity.role()); }; +}; +struct HasSolidInfillVisitor : public HasRoleVisitor{ + void default_use(const ExtrusionEntity &entity) override { found = is_solid_infill(entity.role()); }; +}; + //call simplify for all paths. class SimplifyVisitor : public ExtrusionVisitorRecursive { @@ -726,7 +753,8 @@ class ExtrusionModifyFlow : public ExtrusionVisitorRecursive { #if _DEBUG struct LoopAssertVisitor : public ExtrusionVisitorRecursiveConst { virtual void default_use(const ExtrusionEntity& entity) override {}; - virtual void use(const ExtrusionLoop& loop) override { + virtual void use(const ExtrusionPath &path) override { assert(path.length() > SCALED_EPSILON); } + virtual void use(const ExtrusionLoop &loop) override { for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) { assert(it->polyline.size() >= 2); assert(std::prev(it)->polyline.back() == it->polyline.front()); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 5008b80a01d..3dfae6abbf1 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -342,7 +342,7 @@ Fill::do_gap_fill(const ExPolygons& gapfill_areas, const FillParams& params, Ext for (ThickPolyline poly : polylines_gapfill) { for (coord_t width : poly.points_width) { if (width > params.flow.scaled_width() * 2.2) { - std::cerr << "ERRROR!!!! gapfill width = " << unscaled(width) << " > max_width = " << (params.flow.width() * 2) << "\n"; + BOOST_LOG_TRIVIAL(error) << "ERRROR!!!! gapfill width = " << unscaled(width) << " > max_width = " << (params.flow.width() * 2) << "\n"; } } } diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index e050289a083..90b7fbe17dd 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -432,28 +432,40 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi SeamPosition configured_seam_preference; public: bool also_overhangs = false; + bool also_thin_walls = false; PerimeterCopy(std::vector* regions_out, Polygons* polys, SeamPosition configured_seam) : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polygons(polys) { } - virtual void default_use(const ExtrusionEntity& entity) { }; + virtual void default_use(const ExtrusionEntity& entity) {}; virtual void use(const ExtrusionLoop& loop) override { - ExtrusionRole role = loop.role(); - for (const ExtrusionPath& path : loop.paths) { - if (path.role() == ExtrusionRole::erExternalPerimeter) { - role = ExtrusionRole::erExternalPerimeter; - } - if (path.role() == ExtrusionRole::erOverhangPerimeter && - also_overhangs) { // TODO find a way to search for external overhangs only - role = ExtrusionRole::erOverhangPerimeter; - } - } - - if (role == ExtrusionRole::erExternalPerimeter || (role == ExtrusionRole::erOverhangPerimeter && also_overhangs) - || (is_perimeter(role) && configured_seam_preference == spAllRandom)) { //for random seam alignment, extract all perimeters + if ((configured_seam_preference == spAllRandom && !loop.paths.empty() && + is_perimeter(loop.paths.front().role())) + || (also_thin_walls && loop.role() == erThinWall)) { Points p; loop.collect_points(p); polygons->emplace_back(std::move(p)); corresponding_regions_out->push_back(current_layer_region); + return; + }else { + Points p; + for (const ExtrusionPath &path : loop.paths) { + if (path.role() == ExtrusionRole::erExternalPerimeter) { + path.collect_points(p); + } + if (path.role() == ExtrusionRole::erOverhangPerimeter && + also_overhangs) { // TODO find a way to search for external overhangs only + path.collect_points(p); + } + //if (path.role() == ExtrusionRole::erThinWall && also_thin_walls) { + // path.collect_points(p); // TODO: 2.7: reactivate when it's possible to distinguish between thinwalltravel & thinextrusions + // // currently, only looking for thinwall-only loop + //} + } + + if (!p.empty()) { // for random seam alignment, extract all perimeters + polygons->emplace_back(std::move(p)); + corresponding_regions_out->push_back(current_layer_region); + } } } virtual void use(const ExtrusionEntityCollection& collection) override { @@ -469,20 +481,26 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi visitor.set_current_layer_region(layer_region); ex_entity->visit(visitor); if (polygons.empty()) { - //can happen if the external is fully an overhang - visitor.also_overhangs = true; + // maybe only thin walls? + visitor.also_thin_walls = true; ex_entity->visit(visitor); - visitor.also_overhangs = false; if (polygons.empty()) { - //shouldn't happen + // can happen if the external is fully an overhang + visitor.also_overhangs = true; ex_entity->visit(visitor); - assert(false); - // what to do in this case? - Points p; - ex_entity->collect_points(p); - polygons.emplace_back(std::move(p)); - corresponding_regions_out.push_back(layer_region); + visitor.also_overhangs = false; + if (polygons.empty()) { + // shouldn't happen + ex_entity->visit(visitor); + assert(ex_entity->role() == erThinWall); // no loops + // what to do in this case? + Points p; + ex_entity->collect_points(p); + polygons.emplace_back(std::move(p)); + corresponding_regions_out.push_back(layer_region); + } } + visitor.also_thin_walls = false; } } } diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index a9caed949e1..91ecff72ed6 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -62,11 +62,15 @@ uint16_t LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const assert(region.config().infill_extruder.value > 0); assert(region.config().solid_infill_extruder.value > 0); // 1 based extruder ID. - uint16_t extruder = ((this->extruder_override == 0) ? - (is_infill(extrusions.role()) ? - (is_solid_infill(extrusions.entities().front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) : - region.config().perimeter_extruder.value) : - this->extruder_override); + uint16_t extruder = this->extruder_override; + if (this->extruder_override == 0) + if (HasRoleVisitor::search(extrusions, HasInfillVisitor{})) + if (HasRoleVisitor::search(extrusions, HasSolidInfillVisitor{})) + extruder = region.config().solid_infill_extruder; + else + extruder = region.config().infill_extruder; + else + extruder = region.config().perimeter_extruder.value; return (extruder == 0) ? 0 : extruder - 1; } @@ -248,11 +252,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto for (const ExtrusionEntity *ee : layerm->fills.entities()) { // fill represents infill extrusions of a single island. const auto *fill = dynamic_cast(ee); - //FIXME: if first role is gapfill, please search deeper for another role - ExtrusionRole role = fill->entities().empty() ? erNone : fill->entities().front()->role(); - if (is_solid_infill(role)) + // we search as deep as available, in case there is some gapfill role + if (HasRoleVisitor::search(fill->entities(), HasSolidInfillVisitor{})) has_solid_infill = true; - else if (role != erNone) + else if (HasRoleVisitor::search(fill->entities(), HasInfillVisitor{})) has_infill = true; if (m_print_config_ptr) { diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 503bb12bef1..72d3fe3d2cc 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -3051,9 +3051,10 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions //TODO: find a way to avoid double copy (from EntityCollection to ChangeFlow to searcher.search_result.loop class ChangeFlow : public ExtrusionVisitor { public: + ChangeFlow(coordf_t resolution) : resolution_sqr(resolution * resolution) {} float percent_extrusion; std::vector paths; - Point* first_point = nullptr; + const Point* first_point = nullptr; coordf_t resolution_sqr; virtual void use(ExtrusionPath &path) override { //ensure the loop is continue. @@ -3190,43 +3191,88 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions searcher.search_result.path->polyline.set_points().erase( searcher.search_result.path->polyline.set_points().begin() + searcher.search_result.idx_line + 1, searcher.search_result.path->polyline.set_points().end()); - if (searcher.search_result.loop->paths[searcher.search_result.idx_path].polyline.empty() || - !searcher.search_result.loop->paths[searcher.search_result.idx_path] .polyline.back().coincides_with_epsilon(point)) - searcher.search_result.loop->paths[searcher.search_result.idx_path].polyline.append(point); - if (searcher.search_result.loop->paths[searcher.search_result.idx_path].size() < 2 || - searcher.search_result.loop->paths[searcher.search_result.idx_path].length() < SCALED_EPSILON) { - //if too small, replace - poly_after.points.front() = searcher.search_result.loop->paths[searcher.search_result.idx_path].polyline.front(); - searcher.search_result.loop->paths[searcher.search_result.idx_path] = ExtrusionPath(poly_after, *searcher.search_result.path); - }else{ - searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, + size_t idx_path_before = searcher.search_result.idx_path; + size_t idx_path_to_add = idx_path_before + 1; + //check if the first part of the split polyline is long enough. + assert(!searcher.search_result.loop->paths[idx_path_before].empty()); + if (searcher.search_result.loop->paths[idx_path_before].length() + + searcher.search_result.loop->paths[idx_path_before].polyline.back().distance_to(point) < SCALED_EPSILON) { + //not long enough, move point to first point and destroy it + assert(!searcher.search_result.loop->paths[idx_path_before].empty()); + point = searcher.search_result.loop->paths[idx_path_before].first_point(); + searcher.search_result.loop->paths.erase(searcher.search_result.loop->paths.begin() + idx_path_before); + idx_path_to_add = idx_path_before; + idx_path_before--; + } else { + //long enough + if (!searcher.search_result.loop->paths[idx_path_before].polyline.back().coincides_with_epsilon(point)) { + // add the point to previous path + searcher.search_result.loop->paths[idx_path_before].polyline.append(point); + } else { + //point too near of the start, just move the point + point = searcher.search_result.loop->paths[idx_path_before].polyline.back(); + poly_after.points.front() = point; + } + } + // remove next point if too near to point for the poly_after + if (poly_after.size() > 1 && poly_after.points[0].coincides_with_epsilon(poly_after.points[1])) { + poly_after.points.erase(poly_after.begin() + 1); + } + assert(idx_path_before > searcher.search_result.loop->paths.size() || searcher.search_result.loop->paths[idx_path_before].size() >= 2); + assert(idx_path_before > searcher.search_result.loop->paths.size() || searcher.search_result.loop->paths[idx_path_before].length() > SCALED_EPSILON); + // check if poly_after is big enough to be added + if (poly_after.points.size() <= 1 || poly_after.length() < SCALED_EPSILON) { + //if there is a path after it, then move a little bit the first point + if (searcher.search_result.loop->paths.size() > idx_path_to_add) { + assert(searcher.search_result.loop->paths[idx_path_to_add].first_point().coincides_with_epsilon(point)); + searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front() = point; + // set return point, to move our return point + poly_after.clear(); + poly_after.points.push_back(point); + } else { + //use last point as the end pos + Point end_pt = poly_after.back(); + poly_after.clear(); + poly_after.points.push_back(end_pt); + } + } else { + assert(poly_after.length() > SCALED_EPSILON); + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + idx_path_to_add, ExtrusionPath(poly_after, *searcher.search_result.path)); } - assert(searcher.search_result.loop->paths[searcher.search_result.idx_path].polyline.size() > 1); - assert(poly_after.size() > 1); + assert(idx_path_before > searcher.search_result.loop->paths.size() || searcher.search_result.loop->paths[idx_path_before].polyline.size() > 1); + assert(poly_after.size() > 0); //create thin wall path exttrusion ExtrusionEntityCollection tws; tws.append(Geometry::thin_variable_width({ tw }, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution)), false)); assert(!tws.entities().empty()); - ChangeFlow change_flow; +#if _DEBUG + tws.visit(LoopAssertVisitor{}); +#endif + ChangeFlow change_flow(std::max(scale_t(this->print_config->resolution), SCALED_EPSILON)); if (tws.entities().size() == 1 && tws.entities()[0]->is_loop()) { //loop, just add it change_flow.first_point = &point; change_flow.percent_extrusion = 1; change_flow.use(tws); //add move back - searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + idx_path_to_add, change_flow.paths.begin(), change_flow.paths.end()); //add move to - + if (poly_after.first_point() != point) { + assert(poly_after.first_point().coincides_with_epsilon(point)); + assert(searcher.search_result.loop->paths.size() > idx_path_to_add); + assert(poly_after.first_point().coincides_with_epsilon(searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front())); + searcher.search_result.loop->paths[idx_path_to_add].polyline.set_points().front() = poly_after.first_point(); + } #if _DEBUG searcher.search_result.loop->visit(LoopAssertVisitor{}); #endif } else { //first add the return path //ExtrusionEntityCollection tws_second = tws; // this does a deep copy - change_flow.first_point = &point; + change_flow.first_point = &poly_after.first_point(); // end at the start of the next path change_flow.percent_extrusion = 0.1f; change_flow.use(tws); // tws_second); //does not need the deep copy if the change_flow copy the content instead of re-using it. //force reverse @@ -3234,14 +3280,18 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions path.reverse(); std::reverse(change_flow.paths.begin(), change_flow.paths.end()); //std::reverse(change_flow.paths.begin(), change_flow.paths.end()); - searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, - change_flow.paths.begin(), change_flow.paths.end()); + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + idx_path_to_add, + change_flow.paths.begin(), change_flow.paths.end()); //TODO 2.7:change role by a kind of thinwalltravel that won't be considered for seam //add the real extrusion path - change_flow.first_point = &point; + change_flow.first_point = &point; // start at the end of previous extrusion change_flow.percent_extrusion = 9.f; // 0.9 but as we modified it by 0.1 just before, has to multiply by 10 change_flow.paths = std::vector(); change_flow.use(tws); - searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, +#if _DEBUG + for (ExtrusionPath &path : change_flow.paths) + path.visit(LoopAssertVisitor{}); +#endif + searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + idx_path_to_add, change_flow.paths.begin(), change_flow.paths.end()); #if _DEBUG searcher.search_result.loop->visit(LoopAssertVisitor{}); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index ee19d2753e1..eddfe7038de 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1291,6 +1291,7 @@ namespace SupportMaterialInternal { return true; } else { assert(! ee->is_loop()); + // TODO: a bit dangerous, is there possible to have a bridge infill with mixed role? if (is_bridge(ee->role())) return true; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8baf229aa04..d8fdd9708a5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6307,16 +6307,18 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c for (const ExtrusionEntity *ee : layerm->fills.entities()) { // fill represents infill extrusions of a single island. const auto *fill = dynamic_cast(ee); - if (fill != nullptr && !fill->entities().empty()) + if (fill != nullptr && !fill->entities().empty()) { + bool has_solid_infill = HasRoleVisitor::search(fill->entities(), HasSolidInfillVisitor{}); _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, volume(idx_layer, - is_solid_infill(fill->entities().front()->role()) ? + has_solid_infill ? layerm->region().config().solid_infill_extruder : layerm->region().config().infill_extruder, - is_solid_infill(fill->entities().front()->role()) ? 4 : 3), + has_solid_infill ? 4 : 3), feature_to_volume_map); + } } } } From 16d322ae4119c9ed5c081f72ac126df8b1eb208c Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Jan 2024 16:27:04 +0100 Subject: [PATCH 5/6] clean unsued/harmful code for EEC::chained_path_from --- src/libslic3r/ExtrusionEntityCollection.cpp | 37 +++------------------ src/libslic3r/ExtrusionEntityCollection.hpp | 9 +---- src/libslic3r/GCode.cpp | 14 ++++++-- 3 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index e721f04c75d..66130974f34 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -81,40 +81,11 @@ void ExtrusionEntityCollection::remove(size_t i) this->m_entities.erase(this->m_entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role) -//ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) +void ExtrusionEntityCollection::chained_path_from(const Point &start_near) { - //ExtrusionEntityCollection out; - //if (this->no_sort) { - // out = *this; - //} else { - // if (role == erMixed) - // out = *this; - // else { - // for (const ExtrusionEntity *ee : this->entities()) { - // if (role != erMixed) { - // // The caller wants only paths with a specific extrusion role. - // auto role2 = ee->role(); - // if (role != role2) { - // // This extrusion entity does not match the role asked. - // assert(role2 != erMixed); - // continue; - // } - // } - // out.entities().emplace_back(ee->clone()); - // } - // } - // chain_and_reorder_extrusion_entities(out.entities(), &start_near); - //} - //return out; - // Return a filtered copy of the collection. - ExtrusionEntityCollection out; - out.m_entities = filter_by_extrusion_role(extrusion_entities, role); - // Clone the extrusion entities. - for (ExtrusionEntity* &ptr : out.m_entities) - ptr = ptr->clone(); - chain_and_reorder_extrusion_entities(out.m_entities, &start_near); - return out; + if (this->m_no_sort) + return; + chain_and_reorder_extrusion_entities(this->m_entities, &start_near); } void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 3618c0d33de..c4e9e0d73c7 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -107,14 +107,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed); - ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erNone) const { - if (role == erNone) role = this->role(); - if( this->m_no_sort || (role == erMixed) ) - return *this; - else - return chained_path_from(this->m_entities, start_near, role); - } + void chained_path_from(const Point &start_near); void reverse() override; const Point& first_point() const override { return this->entities().front()->first_point(); } const Point& last_point() const override { return this->entities().back()->last_point(); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d8994c9759e..a1962e6128e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3423,9 +3423,16 @@ LayerResult GCode::process_layer( gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); else if (m_config.temperature.get_at(m_writer.tool()->id()) > 0) // don't set it if disabled gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id()); - gcode += this->extrude_support( + //create support-only collection (by copy) + ExtrusionEntityCollection only_support; // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role)); + only_support.set_can_sort_reverse(instance_to_print.object_by_extruder.support->can_sort(), instance_to_print.object_by_extruder.support->can_reverse()); + only_support.append( + filter_by_extrusion_role(instance_to_print.object_by_extruder.support->entities(), + instance_to_print.object_by_extruder.support_extrusion_role)); + only_support.chained_path_from(m_last_pos); + //it's reaordered, now extrude. + gcode += this->extrude_support(only_support); m_layer = layer_to_print.layer(); m_object_layer_over_raft = object_layer_over_raft; } @@ -4887,7 +4894,8 @@ void GCode::use(const ExtrusionEntityCollection &collection) { next_entity->visit(*this); } } else { - ExtrusionEntityCollection chained = collection.chained_path_from(m_last_pos); + ExtrusionEntityCollection chained = collection; // TODO: 2.7 maybe we can visit a modifiable colelction, so we don't copy it at every step? + chained.chained_path_from(m_last_pos); for (const ExtrusionEntity* next_entity : chained.entities()) { next_entity->visit(*this); } From 0621b6f5f178b3c53e5096b5a0a87033212e3b10 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Jan 2024 17:41:50 +0100 Subject: [PATCH 6/6] Fix wrong compute of width from external periemter spacing. Also better rounding for scripted float. supermerill/SuperSlicer#4082 --- src/libslic3r/Flow.cpp | 8 ++++++++ src/slic3r/GUI/ScriptExecutor.cpp | 14 ++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Flow.cpp b/src/libslic3r/Flow.cpp index d81be3aeef9..26aea7e79a4 100644 --- a/src/libslic3r/Flow.cpp +++ b/src/libslic3r/Flow.cpp @@ -287,6 +287,14 @@ Flow Flow::new_from_config(FlowRole role, const DynamicConfig& print_config, flo if (role == frExternalPerimeter) { config_width = print_config.opt("external_perimeter_extrusion_width"); config_spacing = print_config.opt("external_perimeter_extrusion_spacing"); + // external peri spacing is only half spacing -> transform it into a full spacing + if (!config_spacing.is_phony() && !config_spacing.value == 0) { + double raw_spacing = config_spacing.get_abs_value(nozzle_diameter); + config_spacing.percent = false; + config_spacing.value = rounded_rectangle_extrusion_spacing( + rounded_rectangle_extrusion_width_from_spacing(raw_spacing, layer_height, 0.5f), + layer_height, 1.f); + } overlap = (float)print_config.get_abs_value("external_perimeter_overlap", 1.0); } else if (role == frPerimeter) { config_width = print_config.opt("perimeter_extrusion_width"); diff --git a/src/slic3r/GUI/ScriptExecutor.cpp b/src/slic3r/GUI/ScriptExecutor.cpp index 99bc3b438b1..e22e5eb1310 100644 --- a/src/slic3r/GUI/ScriptExecutor.cpp +++ b/src/slic3r/GUI/ScriptExecutor.cpp @@ -173,10 +173,20 @@ float as_get_float(std::string& key) } } -double round(float f) { +double round(float value) { + double intpart; + if (modf(value, &intpart) == 0.0) { + // shortcut for int + return value; + } std::stringstream ss; + //first, get the int part, to see how many digit it takes + int long10 = 0; + if (intpart > 9) + long10 = (int)std::floor(std::log10(std::abs(intpart))); + //set the usable precision: there is only ~7 decimal digit in a float (15-16 decimal digit in a double) + ss << std::fixed << std::setprecision(7 - long10) << value; double dbl_val; - ss << f; ss >> dbl_val; return dbl_val; }