From bf66c447b12fde018ac1d85b562b6b871a50388c Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 18 Jan 2024 18:54:36 +0100 Subject: [PATCH 1/8] 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/8] 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/8] 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/8] 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 47b2a3ea64d31b6d70f37d62aaa5a5ed54560632 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Jan 2024 16:27:04 +0100 Subject: [PATCH 5/8] clean unsued/harmful code for EEC::chained_path_from fix it_end bug in print.cpp. --- src/libslic3r/Brim.cpp | 2 +- src/libslic3r/ExtrusionEntity.hpp | 83 ------------------ src/libslic3r/ExtrusionEntityCollection.cpp | 37 +------- src/libslic3r/ExtrusionEntityCollection.hpp | 97 ++++++++++++++++++--- src/libslic3r/Fill/Fill.cpp | 4 +- src/libslic3r/Fill/FillBase.cpp | 6 +- src/libslic3r/Fill/FillConcentric.cpp | 2 +- src/libslic3r/Fill/FillRectilinear.cpp | 2 +- src/libslic3r/Fill/FillSmooth.cpp | 2 +- src/libslic3r/GCode.cpp | 25 ++++-- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/Print.cpp | 5 +- src/libslic3r/Print.hpp | 6 +- src/libslic3r/PrintObject.cpp | 9 +- src/libslic3r/SupportMaterial.cpp | 11 ++- 15 files changed, 131 insertions(+), 162 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 84507b89aa9..e68923121c9 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -1259,7 +1259,7 @@ void make_brim_ears(const Print& print, const Flow& flow, const PrintObjectPtrs& //push into extrusions extrusion_entities_append_paths( - out.set_entities(), + out, lines_sorted, erSkirt, float(flow.mm3_per_mm()), diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index d8ab38dc3ec..e417b0daa6b 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -552,89 +552,6 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E polylines.clear(); } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) -{ - dst.reserve(dst.size() + polylines.size()); - for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - if (polyline.back() == polyline.front()) { - ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); - path.polyline = polyline; - dst.emplace_back(new ExtrusionLoop(std::move(path))); - } else { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height, can_reverse); - dst.push_back(extrusion_path); - extrusion_path->polyline = polyline; - } - } -} - -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) -{ - dst.reserve(dst.size() + polylines.size()); - for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - if (polyline.back() == polyline.front()) { - ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); - path.polyline = polyline; - dst.emplace_back(new ExtrusionLoop(std::move(path))); - } else { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height, can_reverse); - dst.push_back(extrusion_path); - extrusion_path->polyline = std::move(polyline); - } - } - polylines.clear(); -} - -inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &loops, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) { - dst.reserve(dst.size() + loops.size()); - for (Polygon & polygon : loops) { - if (polygon.is_valid()) { - ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); - path.polyline.append(polygon.points); - path.polyline.append(path.polyline.front()); - dst.emplace_back(new ExtrusionLoop(std::move(path))); - } - } -} - -inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) -{ - dst.reserve(dst.size() + loops.size()); - for (Polygon &polygon : loops) { - if (polygon.is_valid()) { - ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); - path.polyline.append(std::move(polygon.points)); - path.polyline.append(path.polyline.front()); - ExtrusionLoop *loop = new ExtrusionLoop(std::move(path)); - //default to ccw - loop->make_counter_clockwise(); - dst.emplace_back(loop); - } - } - loops.clear(); -} - -inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) -{ - dst.reserve(dst.size() + polylines.size()); - for (Polyline &polyline : polylines) { - if (polyline.is_valid()) { - if (polyline.is_closed()) { - ExtrusionPath extrusion_path(role, mm3_per_mm, width, height, can_reverse); - extrusion_path.polyline = std::move(polyline); - dst.emplace_back(new ExtrusionLoop(std::move(extrusion_path))); - } else { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height, can_reverse); - extrusion_path->polyline = std::move(polyline); - dst.emplace_back(extrusion_path); - } - } - } - polylines.clear(); -} - class ExtrusionPrinter : public ExtrusionVisitorConst { std::stringstream ss; double mult; 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..247c702b182 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -36,6 +36,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity /// Owned ExtrusionEntities and descendent ExtrusionEntityCollections. /// Iterating over this needs to check each child to see if it, too is a collection. + /// FIXME Warning: not a true const, the entities inside can be modified, and if the entities are deleted -> crash const ExtrusionEntitiesPtr& entities() const { return m_entities; } ExtrusionEntitiesPtr& set_entities() { return m_entities; } ExtrusionEntityCollection() : m_no_sort(false), ExtrusionEntity(true) {} @@ -54,8 +55,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity ~ExtrusionEntityCollection() override { clear(); } // move all entitites from src into this void append_move_from(ExtrusionEntityCollection &src) { - m_entities.insert(m_entities.end(), src.m_entities.begin(), src.m_entities.end()); - src.m_entities.clear(); + this->append(std::move(src.m_entities)); + src.m_entities = {}; } /// Operator to convert and flatten this collection to a single vector of ExtrusionPaths. @@ -107,14 +108,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(); } @@ -188,6 +182,89 @@ class FlatenEntities : public ExtrusionVisitorConst { virtual void use(const ExtrusionEntityCollection &coll) override; }; +inline void extrusion_entities_append_paths(ExtrusionEntityCollection &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +{ + //dst.reserve(dst.size() + polylines.size()); + for (Polyline &polyline : polylines) + if (polyline.is_valid()) { + if (polyline.back() == polyline.front()) { + ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); + path.polyline = polyline; + dst.append(ExtrusionLoop(std::move(path))); + } else { + ExtrusionPath extrusion_path(role, mm3_per_mm, width, height, can_reverse); + extrusion_path.polyline = polyline; + dst.append(std::move(extrusion_path)); + } + } +} + +inline void extrusion_entities_append_paths(ExtrusionEntityCollection &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +{ + //dst.reserve(dst.size() + polylines.size()); + for (Polyline &polyline : polylines) + if (polyline.is_valid()) { + if (polyline.back() == polyline.front()) { + ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); + path.polyline = polyline; + dst.append(ExtrusionLoop(std::move(path))); + } else { + ExtrusionPath extrusion_path(role, mm3_per_mm, width, height, can_reverse); + extrusion_path.polyline = std::move(polyline); + dst.append(std::move(extrusion_path)); + } + } + polylines.clear(); +} + +inline void extrusion_entities_append_loops(ExtrusionEntityCollection &dst, Polygons &loops, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) { + //dst.reserve(dst.size() + loops.size()); + for (Polygon & polygon : loops) { + if (polygon.is_valid()) { + ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); + path.polyline.append(polygon.points); + path.polyline.append(path.polyline.front()); + dst.append(ExtrusionLoop(std::move(path))); + } + } +} + +inline void extrusion_entities_append_loops(ExtrusionEntityCollection &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +{ + //dst.reserve(dst.size() + loops.size()); + for (Polygon &polygon : loops) { + if (polygon.is_valid()) { + ExtrusionPath path(role, mm3_per_mm, width, height, can_reverse); + path.polyline.append(std::move(polygon.points)); + path.polyline.append(path.polyline.front()); + ExtrusionLoop loop(std::move(path)); + //default to ccw + loop.make_counter_clockwise(); + dst.append(std::move(loop)); + } + } + loops.clear(); +} + +inline void extrusion_entities_append_loops_and_paths(ExtrusionEntityCollection &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +{ + //dst.reserve(dst.size() + polylines.size()); + for (Polyline &polyline : polylines) { + if (polyline.is_valid()) { + if (polyline.is_closed()) { + ExtrusionPath extrusion_path(role, mm3_per_mm, width, height, can_reverse); + extrusion_path.polyline = std::move(polyline); + dst.append(ExtrusionLoop(std::move(extrusion_path))); + } else { + ExtrusionPath extrusion_path(role, mm3_per_mm, width, height, can_reverse); + extrusion_path.polyline = std::move(polyline); + dst.append(std::move(extrusion_path)); + } + } + } + polylines.clear(); +} + #ifdef _DEBUG class TestCollection : public ExtrusionVisitorRecursiveConst { public: diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index eb2dfc79b92..7347aff31ca 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -500,7 +500,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: eec->set_can_sort_reverse(false, false); for (ExtrusionEntityCollection* per_priority : fills_by_priority) { if (!per_priority->entities().empty()) - eec->set_entities().push_back(per_priority); + eec->append(ExtrusionEntitiesPtr{per_priority}); else delete per_priority; } @@ -956,7 +956,7 @@ void Layer::make_ironing() // Don't sort the ironing infill lines as they are monotonicly ordered. eec->set_can_sort_reverse(false, false); extrusion_entities_append_paths( - eec->set_entities(), std::move(polylines), + *eec, std::move(polylines), erIroning, //FIXME FLOW decide if it's good flow_mm3_per_mm, extrusion_width/*float(flow.width())*/, float(extrusion_height)/*float(height)*/); diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 3dfae6abbf1..3538e777f23 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -293,7 +293,7 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); /// push the path extrusion_entities_append_paths( - eec->set_entities(), std::move(simple_polylines), + *eec, std::move(simple_polylines), good_role, params.flow.mm3_per_mm()* params.flow_mult * mult_flow, (float)(params.flow.width()* params.flow_mult * mult_flow), @@ -3676,7 +3676,7 @@ FillWithPerimeter::fill_surface_extrusion(const Surface* surface, const FillPara ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); /// push the path extrusion_entities_append_paths( - eec_peri->set_entities(), + *eec_peri, polylines_peri, good_role, params.flow.mm3_per_mm() * params.flow_mult, @@ -3700,7 +3700,7 @@ FillWithPerimeter::fill_surface_extrusion(const Surface* surface, const FillPara ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); /// push the path extrusion_entities_append_paths( - eec_infill->set_entities(), + *eec_infill, polys_infill, good_role, params.flow.mm3_per_mm() * params.flow_mult, diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index a20ffa9e5e8..3afd1c357ec 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -260,7 +260,7 @@ FillConcentricWGapFill::fill_surface_extrusion( leaf_coll->append(ExtrusionEntityCollection{}); leaf_count.sortable = static_cast(leaf_coll->set_entities().back()); ExtrusionEntityCollection new_coll_nosort{ false, false }; - new_coll_nosort.set_entities().push_back(elt); + new_coll_nosort.append(ExtrusionEntitiesPtr{elt}); leaf_count.sortable->append(std::move(new_coll_nosort)); } if (leaf_count.sortable) { diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index e21dcb5cfe6..6bc1ecae6d7 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -3499,7 +3499,7 @@ FillRectilinearWGapFill::fill_surface_extrusion(const Surface *surface, const Fi eec->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); extrusion_entities_append_paths( - eec->set_entities(), polylines_rectilinear, + *eec, polylines_rectilinear, good_role, params.flow.mm3_per_mm() * params.flow_mult, params.flow.width() * params.flow_mult, diff --git a/src/libslic3r/Fill/FillSmooth.cpp b/src/libslic3r/Fill/FillSmooth.cpp index 972e89b2c82..7f9ec7e4ab6 100644 --- a/src/libslic3r/Fill/FillSmooth.cpp +++ b/src/libslic3r/Fill/FillSmooth.cpp @@ -115,7 +115,7 @@ namespace Slic3r { } extrusion_entities_append_paths( - eec.set_entities(), std::move(polylines_layer), + eec, std::move(polylines_layer), good_role, params.flow.mm3_per_mm() * params.flow_mult * mult_flow, //min-reduced flow width for a better view (it's mostly a gui thing, but some support code can want to mess with it) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d8994c9759e..0ef9e1a00e3 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2894,7 +2894,7 @@ std::regex regex_g92e0_gcode{ "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]* // and performing the extruder specific extrusions together. LayerResult GCode::process_layer( const Print &print, - Print::StatusMonitor &status_monitor, + Print::StatusMonitor &status_monitor, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, const LayerTools &layer_tools, @@ -3423,9 +3423,15 @@ 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( - // 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)); + // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. + // note: filter_by_extrusion_role doesn't copy, so please don't modify / remove these entities + ExtrusionEntitiesPtr only_support = + filter_by_extrusion_role(instance_to_print.object_by_extruder.support->entities(), + instance_to_print.object_by_extruder.support_extrusion_role); + if (instance_to_print.object_by_extruder.support->can_sort()) + chain_and_reorder_extrusion_entities(only_support, &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 +4893,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); } @@ -5074,20 +5081,20 @@ std::string GCode::extrude_ironing(const Print& print, const std::vectorrole(); assert(role == erSupportMaterial || role == erSupportMaterialInterface || role == erMixed); if (const ExtrusionEntityCollection* coll = dynamic_cast(ee)) { - gcode += extrude_support(*coll); + gcode += extrude_support(coll->entities()); continue; } const char *label = (role == erSupportMaterial) ? support_label : support_interface_label; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 56e427b5453..ccd71b884d0 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -384,7 +384,7 @@ class GCode : ExtrusionVisitorConst { std::string extrude_perimeters(const Print &print, const std::vector &by_region); std::string extrude_infill(const Print& print, const std::vector& by_region, bool is_infill_first); std::string extrude_ironing(const Print& print, const std::vector& by_region); - std::string extrude_support(const ExtrusionEntityCollection &support_fills); + std::string extrude_support(const ExtrusionEntitiesPtr &support_fills); Polyline travel_to(std::string& gcode, const Point &point, ExtrusionRole role); void write_travel_to(std::string& gcode, const Polyline& travel, std::string comment); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 19c1e232561..fa9e9e14cd6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1608,9 +1608,8 @@ void Print::_make_wipe_tower() if (idx_begin != size_t(-1)) { // Find the position in m_objects.first()->support_layers to insert these new support layers. double wipe_tower_new_layer_print_z_first = m_wipe_tower_data.tool_ordering.layer_tools()[idx_begin].print_z; - SupportLayerPtrs::const_iterator it_layer = m_objects.front()->support_layers().begin(); - SupportLayerPtrs::const_iterator it_end = m_objects.front()->support_layers().end(); - for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); + SupportLayerPtrs::const_iterator it_layer = m_objects.front()->edit_support_layers().begin(); + for (; it_layer != m_objects.front()->edit_support_layers().end() && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer. for (size_t i = idx_begin; i < idx_end; ++ i) { LayerTools < = const_cast(m_wipe_tower_data.tool_ordering.layer_tools()[i]); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 6eec0dc90d4..6cba4c389a7 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -269,7 +269,7 @@ class PrintObject : public PrintObjectBaseWithStategenerate_toolpaths(object.support_layers(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); + this->generate_toolpaths(object.edit_support_layers(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); #ifdef SLIC3R_DEBUG { @@ -3567,8 +3567,7 @@ struct MyLayerExtruded *m_polygons_to_extrude = union_safety_offset(*m_polygons_to_extrude); } // 2) Merge the extrusions. - this->extrusions.set_entities().insert(this->extrusions.entities().end(), other.extrusions.entities().begin(), other.extrusions.entities().end()); - other.extrusions.set_entities().clear(); + this->extrusions.append_move_from(other.extrusions); // 3) Merge the infill polygons. Slic3r::polygons_append(this->layer->polygons, std::move(other.layer->polygons)); this->layer->polygons = union_safety_offset(this->layer->polygons); @@ -3818,7 +3817,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const // Transform loops into ExtrusionPath objects. extrusion_entities_append_paths( - top_contact_layer.extrusions.set_entities(), + top_contact_layer.extrusions, std::move(loop_lines), erSupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); } @@ -4107,7 +4106,7 @@ void modulate_extrusion_by_overlapping_layers( // If there are any non-consumed fragments, add them separately. //FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected. for (auto it_fragment = path_fragments.begin(); it_fragment != path_fragments.end(); ++ it_fragment) - extrusion_entities_append_paths(extrusions_in_out.set_entities(), std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); + extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); } void PrintObjectSupportMaterial::generate_toolpaths( From 0aa1d748e740591dbb10b483e58588c7f7b14d53 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 23 Jan 2024 17:41:50 +0100 Subject: [PATCH 6/8] 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; } From 9e3b1746253e2a8b546b79edf65fb39265cf0219 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 24 Jan 2024 09:30:49 +0100 Subject: [PATCH 7/8] update profiles --- resources/profiles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles b/resources/profiles index be9eda0bbf9..22a3c028573 160000 --- a/resources/profiles +++ b/resources/profiles @@ -1 +1 @@ -Subproject commit be9eda0bbf93ff217ad03606b0f9843aba145fa8 +Subproject commit 22a3c0285734468a340b7d798cb387b392e32789 From 46d51e57fa76468914c49cd4d1d770232378e227 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 24 Jan 2024 11:33:13 +0100 Subject: [PATCH 8/8] new setting: gcode_ascii for ascii-only output (replace other char by '_') supermerill/SuperSlicer#4070 --- resources/ui_layout/default/printer_fff.ui | 5 ++++- src/libslic3r/GCode.cpp | 4 ++++ src/libslic3r/GCode.hpp | 2 ++ src/libslic3r/LocalesUtils.cpp | 26 ++++++++++++++++++++++ src/libslic3r/LocalesUtils.hpp | 2 ++ src/libslic3r/Preset.cpp | 1 + src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 9 ++++++++ src/libslic3r/PrintConfig.hpp | 1 + 9 files changed, 50 insertions(+), 1 deletion(-) diff --git a/resources/ui_layout/default/printer_fff.ui b/resources/ui_layout/default/printer_fff.ui index 36a9a8142d9..15324b1a31e 100644 --- a/resources/ui_layout/default/printer_fff.ui +++ b/resources/ui_layout/default/printer_fff.ui @@ -36,7 +36,10 @@ group:silent_mode_event:Firmware setting:arc_fitting setting:arc_fitting_tolerance end_line - setting:gcode_filename_illegal_char + line:Formatting + setting:gcode_filename_illegal_char + setting:gcode_ascii + end_line group:Cooling fan setting:fan_printer_min_speed line:Speedup time diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0ef9e1a00e3..c6c32102793 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1328,6 +1328,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene m_find_replace = make_unique(print.config()); file.set_find_replace(m_find_replace.get(), false); } + file.set_only_ascii(print.config().gcode_ascii.value); // resets analyzer's tracking data m_last_height = 0.f; @@ -5145,6 +5146,9 @@ void GCode::GCodeOutputStream::write(const char *what) if (what != nullptr) { //FIXME don't allocate a string, maybe process a batch of lines? std::string gcode(m_find_replace ? m_find_replace->process_layer(what) : what); + if (m_only_ascii) { + remove_not_ascii(gcode); + } // writes string to file fwrite(gcode.c_str(), 1, gcode.size(), this->f); m_processor.process_buffer(gcode); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index ccd71b884d0..b185e5942be 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -214,6 +214,7 @@ class GCode : ExtrusionVisitorConst { // It is being set to null inside process_layers(), because the find-replace process // is being called on a secondary thread to improve performance. void set_find_replace(GCodeFindReplace *find_replace, bool enabled) { m_find_replace_backup = find_replace; m_find_replace = enabled ? find_replace : nullptr; } + void set_only_ascii(bool only_ascii) { m_only_ascii = only_ascii; } void find_replace_enable() { m_find_replace = m_find_replace_backup; } void find_replace_supress() { m_find_replace = nullptr; } @@ -239,6 +240,7 @@ class GCode : ExtrusionVisitorConst { FILE *f { nullptr }; // Find-replace post-processor to be called before GCodePostProcessor. GCodeFindReplace *m_find_replace { nullptr }; + bool m_only_ascii; // If suppressed, the backoup holds m_find_replace. GCodeFindReplace *m_find_replace_backup { nullptr }; GCodeProcessor &m_processor; diff --git a/src/libslic3r/LocalesUtils.cpp b/src/libslic3r/LocalesUtils.cpp index 5bf520568a2..f189466e017 100644 --- a/src/libslic3r/LocalesUtils.cpp +++ b/src/libslic3r/LocalesUtils.cpp @@ -124,6 +124,32 @@ std::string float_to_string_decimal_point(double value, int precision/* = -1*/) return to_string_nozero(value, precision < 0 ? 6 : precision); } +void remove_not_ascii(std::string &tomodify) { + size_t pos_read = 0; + bool previous_ascii = true; + //skip until a not-ascii character + while (pos_read < tomodify.length() && ((tomodify[pos_read] & 0x80) == 0)) { ++pos_read; } + size_t pos_write = pos_read; + //then modify the string + while (pos_read < tomodify.length()) { + if ((tomodify[pos_read] & 0x80) == 0) { + //ascii, write + tomodify[pos_write] = tomodify[pos_read]; + ++pos_write; + previous_ascii = true; + } else { + //not-ascii, remove + if (previous_ascii) { + tomodify[pos_write] = '_'; + ++pos_write; + } + previous_ascii = false; + } + ++pos_read; + } + //remove extra bits + tomodify.resize(pos_write); +} } // namespace Slic3r diff --git a/src/libslic3r/LocalesUtils.hpp b/src/libslic3r/LocalesUtils.hpp index 18ec1102bba..c7e5abdddfb 100644 --- a/src/libslic3r/LocalesUtils.hpp +++ b/src/libslic3r/LocalesUtils.hpp @@ -37,6 +37,8 @@ bool is_decimal_separator_point(); std::string to_string_nozero(double value, int32_t max_precision); +void remove_not_ascii(std::string &tomodify); + // A substitute for std::to_string that works according to // C++ locales, not C locale. Meant to be used when we need // to be sure that decimal point is used as a separator. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 52a0c4e872e..fbcb25e6b2e 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -810,6 +810,7 @@ static std::vector s_Preset_printer_options { "fan_speedup_time", "fan_percentage", "fan_printer_min_speed", + "gcode_ascii", "gcode_filename_illegal_char", "gcode_flavor", "gcode_precision_xyz", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index fa9e9e14cd6..ab63b9f9c54 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -123,6 +123,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "gap_fill_fan_speed", "gap_fill_flow_match_perimeter", "gap_fill_speed", + "gcode_ascii", "gcode_comments", "gcode_filename_illegal_char", "gcode_label_objects", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b72026d503b..8fab71f50df 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2833,6 +2833,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comPrusa; def->set_default_value(new ConfigOptionFloatOrPercent(50,true)); + def = this->add("gcode_ascii", coBool); + def->label = L("Only ascii characters in gcode"); + def->category = OptionCategory::firmware; + def->tooltip = L("When printing the gcode file, replace any non-ascii character by a '_'." + " Can be useful if the firmware or a software in a workflow doesn't support uft-8."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("gcode_comments", coBool); def->label = L("Verbose G-code"); def->category = OptionCategory::output; @@ -7901,6 +7909,7 @@ std::unordered_set prusa_export_to_remove_keys = { "first_layer_infill_speed", "first_layer_min_speed", "first_layer_size_compensation_layers", +"gcode_ascii", "gap_fill_acceleration", "gap_fill_extension", "gap_fill_fan_speed", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index e4361227a01..647c63e501c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1041,6 +1041,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, filament_wipe_advanced_pigment)) ((ConfigOptionFloats, filament_cooling_final_speed)) ((ConfigOptionStrings, filament_ramming_parameters)) + ((ConfigOptionBool, gcode_ascii)) ((ConfigOptionBool, gcode_comments)) ((ConfigOptionString, gcode_filename_illegal_char)) ((ConfigOptionEnum, gcode_flavor))